Commit 4046136a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-3.16-rc1' of...

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
  ...
parents b55a0ff8 a100d88d
...@@ -24,7 +24,8 @@ netlink based networking for inter-process communication in a significantly ...@@ -24,7 +24,8 @@ netlink based networking for inter-process communication in a significantly
easier way: easier way:
int cn_add_callback(struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *)); int cn_add_callback(struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *));
void cn_netlink_send(struct cn_msg *msg, u32 __group, int gfp_mask); void cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __group, int gfp_mask);
void cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, int gfp_mask);
struct cb_id struct cb_id
{ {
...@@ -71,15 +72,21 @@ void cn_del_callback(struct cb_id *id); ...@@ -71,15 +72,21 @@ void cn_del_callback(struct cb_id *id);
struct cb_id *id - unique connector's user identifier. struct cb_id *id - unique connector's user identifier.
int cn_netlink_send(struct cn_msg *msg, u32 __groups, int gfp_mask); int cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __groups, int gfp_mask);
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __groups, int gfp_mask);
Sends message to the specified groups. It can be safely called from Sends message to the specified groups. It can be safely called from
softirq context, but may silently fail under strong memory pressure. softirq context, but may silently fail under strong memory pressure.
If there are no listeners for given group -ESRCH can be returned. If there are no listeners for given group -ESRCH can be returned.
struct cn_msg * - message header(with attached data). struct cn_msg * - message header(with attached data).
u16 len - for *_multi multiple cn_msg messages can be sent
u32 port - destination port.
If non-zero the message will be sent to the
given port, which should be set to the
original sender.
u32 __group - destination group. u32 __group - destination group.
If __group is zero, then appropriate group will If port and __group is zero, then appropriate group will
be searched through all registered connector users, be searched through all registered connector users,
and message will be delivered to the group which was and message will be delivered to the group which was
created for user with the same ID as in msg. created for user with the same ID as in msg.
...@@ -111,7 +118,7 @@ acknowledge number MUST be the same + 1. ...@@ -111,7 +118,7 @@ acknowledge number MUST be the same + 1.
If we receive a message and its sequence number is not equal to one we If we receive a message and its sequence number is not equal to one we
are expecting, then it is a new message. If we receive a message and are expecting, then it is a new message. If we receive a message and
its sequence number is the same as one we are expecting, but its its sequence number is the same as one we are expecting, but its
acknowledge is not equal to the acknowledge number in the original acknowledge is not equal to the sequence number in the original
message + 1, then it is a new message. message + 1, then it is a new message.
Obviously, the protocol header contains the above id. Obviously, the protocol header contains the above id.
......
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. ...@@ -26,7 +26,7 @@ Each child node must have one and only one 'reg' entry of type SPMI_USID.
reg = <...>; reg = <...>;
#address-cells = <2>; #address-cells = <2>;
#size-cells <0>; #size-cells = <0>;
child@0 { child@0 {
compatible = "..."; compatible = "...";
......
...@@ -82,7 +82,7 @@ driver - (standard) symlink to the w1 driver ...@@ -82,7 +82,7 @@ driver - (standard) symlink to the w1 driver
w1_master_add - Manually register a slave device w1_master_add - Manually register a slave device
w1_master_attempts - the number of times a search was attempted w1_master_attempts - the number of times a search was attempted
w1_master_max_slave_count w1_master_max_slave_count
- the maximum slaves that may be attached to a master - maximum number of slaves to search for at a time
w1_master_name - the name of the device (w1_bus_masterX) w1_master_name - the name of the device (w1_bus_masterX)
w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled
w1_master_remove - Manually remove a slave device w1_master_remove - Manually remove a slave device
......
...@@ -30,7 +30,7 @@ Protocol. ...@@ -30,7 +30,7 @@ Protocol.
W1_SLAVE_CMD W1_SLAVE_CMD
userspace command for slave device userspace command for slave device
(read/write/touch) (read/write/touch)
__u8 res - reserved __u8 status - error indication from kernel
__u16 len - size of data attached to this header data __u16 len - size of data attached to this header data
union { union {
__u8 id[8]; - slave unique device id __u8 id[8]; - slave unique device id
...@@ -44,10 +44,14 @@ Protocol. ...@@ -44,10 +44,14 @@ Protocol.
__u8 cmd - command opcode. __u8 cmd - command opcode.
W1_CMD_READ - read command W1_CMD_READ - read command
W1_CMD_WRITE - write command W1_CMD_WRITE - write command
W1_CMD_TOUCH - touch command
(write and sample data back to userspace)
W1_CMD_SEARCH - search command W1_CMD_SEARCH - search command
W1_CMD_ALARM_SEARCH - alarm search command W1_CMD_ALARM_SEARCH - alarm search command
W1_CMD_TOUCH - touch command
(write and sample data back to userspace)
W1_CMD_RESET - send bus reset
W1_CMD_SLAVE_ADD - add slave to kernel list
W1_CMD_SLAVE_REMOVE - remove slave from kernel list
W1_CMD_LIST_SLAVES - get slaves list from kernel
__u8 res - reserved __u8 res - reserved
__u16 len - length of data for this command __u16 len - length of data for this command
For read command data must be allocated like for write command For read command data must be allocated like for write command
...@@ -87,8 +91,7 @@ format: ...@@ -87,8 +91,7 @@ format:
id0 ... idN id0 ... idN
Each message is at most 4k in size, so if number of master devices Each message is at most 4k in size, so if number of master devices
exceeds this, it will be split into several messages, exceeds this, it will be split into several messages.
cn.seq will be increased for each one.
W1 search and alarm search commands. W1 search and alarm search commands.
request: request:
......
...@@ -2188,6 +2188,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git ...@@ -2188,6 +2188,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
S: Supported S: Supported
F: drivers/char/* F: drivers/char/*
F: drivers/misc/* F: drivers/misc/*
F: include/linux/miscdevice.h
CHECKPATCH CHECKPATCH
M: Andy Whitcroft <apw@canonical.com> M: Andy Whitcroft <apw@canonical.com>
......
...@@ -83,7 +83,6 @@ obj-$(CONFIG_PCCARD) += pcmcia/ ...@@ -83,7 +83,6 @@ obj-$(CONFIG_PCCARD) += pcmcia/
obj-$(CONFIG_DIO) += dio/ obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/ obj-$(CONFIG_SBUS) += sbus/
obj-$(CONFIG_ZORRO) += zorro/ obj-$(CONFIG_ZORRO) += zorro/
obj-$(CONFIG_MAC) += macintosh/
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_TC) += tc/
...@@ -141,7 +140,6 @@ obj-y += clk/ ...@@ -141,7 +140,6 @@ obj-y += clk/
obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_MAILBOX) += mailbox/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
obj-$(CONFIG_NFC) += nfc/
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-$(CONFIG_RPMSG) += rpmsg/ obj-$(CONFIG_RPMSG) += rpmsg/
......
...@@ -345,7 +345,6 @@ static int __init applicom_init(void) ...@@ -345,7 +345,6 @@ static int __init applicom_init(void)
free_irq(apbs[i].irq, &dummy); free_irq(apbs[i].irq, &dummy);
iounmap(apbs[i].RamIO); iounmap(apbs[i].RamIO);
} }
pci_disable_device(dev);
return ret; return ret;
} }
......
...@@ -43,6 +43,8 @@ static struct cn_dev cdev; ...@@ -43,6 +43,8 @@ static struct cn_dev cdev;
static int cn_already_initialized; static int cn_already_initialized;
/* /*
* Sends mult (multiple) cn_msg at a time.
*
* msg->seq and msg->ack are used to determine message genealogy. * msg->seq and msg->ack are used to determine message genealogy.
* When someone sends message it puts there locally unique sequence * When someone sends message it puts there locally unique sequence
* and random acknowledge numbers. Sequence number may be copied into * and random acknowledge numbers. Sequence number may be copied into
...@@ -62,10 +64,13 @@ static int cn_already_initialized; ...@@ -62,10 +64,13 @@ static int cn_already_initialized;
* the acknowledgement number in the original message + 1, then it is * the acknowledgement number in the original message + 1, then it is
* a new message. * a new message.
* *
* If msg->len != len, then additional cn_msg messages are expected following
* the first msg.
*
* The message is sent to, the portid if given, the group if given, both if * The message is sent to, the portid if given, the group if given, both if
* both, or if both are zero then the group is looked up and sent there. * both, or if both are zero then the group is looked up and sent there.
*/ */
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
gfp_t gfp_mask) gfp_t gfp_mask)
{ {
struct cn_callback_entry *__cbq; struct cn_callback_entry *__cbq;
...@@ -98,7 +103,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, ...@@ -98,7 +103,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
if (!portid && !netlink_has_listeners(dev->nls, group)) if (!portid && !netlink_has_listeners(dev->nls, group))
return -ESRCH; return -ESRCH;
size = sizeof(*msg) + msg->len; size = sizeof(*msg) + len;
skb = nlmsg_new(size, gfp_mask); skb = nlmsg_new(size, gfp_mask);
if (!skb) if (!skb)
...@@ -121,6 +126,14 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, ...@@ -121,6 +126,14 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
gfp_mask); gfp_mask);
return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT)); return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
} }
EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
/* same as cn_netlink_send_mult except msg->len is used for len */
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
gfp_t gfp_mask)
{
return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
}
EXPORT_SYMBOL_GPL(cn_netlink_send); EXPORT_SYMBOL_GPL(cn_netlink_send);
/* /*
......
...@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK ...@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
Say Y here to enable extcon device driver based on ADC values. Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX14577 config EXTCON_MAX14577
tristate "MAX14577 EXTCON Support" tristate "MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577 depends on MFD_MAX14577
select IRQ_DOMAIN select IRQ_DOMAIN
select REGMAP_I2C select REGMAP_I2C
help help
If you say yes here you get support for the MUIC device of If you say yes here you get support for the MUIC device of
Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
detector and switch. detector and switch.
config EXTCON_MAX77693 config EXTCON_MAX77693
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* @chan: iio channel being queried. * @chan: iio channel being queried.
*/ */
struct adc_jack_data { struct adc_jack_data {
struct extcon_dev edev; struct extcon_dev *edev;
const char **cable_names; const char **cable_names;
int num_cables; int num_cables;
...@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work) ...@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
ret = iio_read_channel_raw(data->chan, &adc_val); ret = iio_read_channel_raw(data->chan, &adc_val);
if (ret < 0) { if (ret < 0) {
dev_err(&data->edev.dev, "read channel() error: %d\n", ret); dev_err(&data->edev->dev, "read channel() error: %d\n", ret);
return; return;
} }
...@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work) ...@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)
} }
/* if no def has met, it means state = 0 (no cables attached) */ /* if no def has met, it means state = 0 (no cables attached) */
extcon_set_state(&data->edev, state); extcon_set_state(data->edev, state);
} }
static irqreturn_t adc_jack_irq_thread(int irq, void *_data) static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
...@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->edev.name = pdata->name;
if (!pdata->cable_names) { if (!pdata->cable_names) {
err = -EINVAL;
dev_err(&pdev->dev, "error: cable_names not defined.\n"); dev_err(&pdev->dev, "error: cable_names not defined.\n");
goto out; return -EINVAL;
} }
data->edev.dev.parent = &pdev->dev; data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
data->edev.supported_cable = pdata->cable_names; if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
data->edev->dev.parent = &pdev->dev;
data->edev->name = pdata->name;
/* Check the length of array and set num_cables */ /* Check the length of array and set num_cables */
for (i = 0; data->edev.supported_cable[i]; i++) for (i = 0; data->edev->supported_cable[i]; i++)
; ;
if (i == 0 || i > SUPPORTED_CABLE_MAX) { if (i == 0 || i > SUPPORTED_CABLE_MAX) {
err = -EINVAL;
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
i - 1); i - 1);
goto out; return -EINVAL;
} }
data->num_cables = i; data->num_cables = i;
if (!pdata->adc_conditions || if (!pdata->adc_conditions ||
!pdata->adc_conditions[0].state) { !pdata->adc_conditions[0].state) {
err = -EINVAL;
dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
goto out; return -EINVAL;
} }
data->adc_conditions = pdata->adc_conditions; data->adc_conditions = pdata->adc_conditions;
...@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
data->num_conditions = i; data->num_conditions = i;
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel); data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
if (IS_ERR(data->chan)) { if (IS_ERR(data->chan))
err = PTR_ERR(data->chan); return PTR_ERR(data->chan);
goto out;
}
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
...@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
err = extcon_dev_register(&data->edev); err = devm_extcon_dev_register(&pdev->dev, data->edev);
if (err) if (err)
goto out; return err;
data->irq = platform_get_irq(pdev, 0); data->irq = platform_get_irq(pdev, 0);
if (!data->irq) { if (!data->irq) {
dev_err(&pdev->dev, "platform_get_irq failed\n"); dev_err(&pdev->dev, "platform_get_irq failed\n");
err = -ENODEV; return -ENODEV;
goto err_irq;
} }
err = request_any_context_irq(data->irq, adc_jack_irq_thread, err = request_any_context_irq(data->irq, adc_jack_irq_thread,
...@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev) ...@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "error: irq %d\n", data->irq); dev_err(&pdev->dev, "error: irq %d\n", data->irq);
goto err_irq; return err;
} }
return 0; return 0;
err_irq:
extcon_dev_unregister(&data->edev);
out:
return err;
} }
static int adc_jack_remove(struct platform_device *pdev) static int adc_jack_remove(struct platform_device *pdev)
...@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev) ...@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)
free_irq(data->irq, data); free_irq(data->irq, data);
cancel_work_sync(&data->handler.work); cancel_work_sync(&data->handler.work);
extcon_dev_unregister(&data->edev);
return 0; return 0;
} }
......
...@@ -91,7 +91,7 @@ struct arizona_extcon_info { ...@@ -91,7 +91,7 @@ struct arizona_extcon_info {
int hpdet_ip; int hpdet_ip;
struct extcon_dev edev; struct extcon_dev *edev;
}; };
static const struct arizona_micd_config micd_default_modes[] = { static const struct arizona_micd_config micd_default_modes[] = {
...@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) ...@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
} }
/* If the cable was removed while measuring ignore the result */ /* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret); ret);
...@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data) ...@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
else else
report = ARIZONA_CABLE_HEADPHONE; report = ARIZONA_CABLE_HEADPHONE;
ret = extcon_set_cable_state_(&info->edev, report, true); ret = extcon_set_cable_state_(info->edev, report, true);
if (ret != 0) if (ret != 0)
dev_err(arizona->dev, "Failed to report HP/line: %d\n", dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret); ret);
...@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info) ...@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */ /* Just report headphone */
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE, 1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE); 1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0) if (ret != 0)
...@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info) ...@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */ /* Just report headphone */
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE, 1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE); 1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0) if (ret != 0)
...@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work) ...@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
mutex_lock(&info->lock); mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */ /* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL); ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n", dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret); ret);
...@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work) ...@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
arizona_identify_headphone(info); arizona_identify_headphone(info);
ret = extcon_update_state(&info->edev, ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_MICROPHONE, 1 << ARIZONA_CABLE_MICROPHONE,
1 << ARIZONA_CABLE_MICROPHONE); 1 << ARIZONA_CABLE_MICROPHONE);
...@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ...@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (info->last_jackdet == present) { if (info->last_jackdet == present) {
dev_dbg(arizona->dev, "Detected jack\n"); dev_dbg(arizona->dev, "Detected jack\n");
ret = extcon_set_cable_state_(&info->edev, ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_MECHANICAL, true); ARIZONA_CABLE_MECHANICAL, true);
if (ret != 0) if (ret != 0)
...@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) ...@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
info->micd_ranges[i].key, 0); info->micd_ranges[i].key, 0);
input_sync(info->input); input_sync(info->input);
ret = extcon_update_state(&info->edev, 0xffffffff, 0); ret = extcon_update_state(info->edev, 0xffffffff, 0);
if (ret != 0) if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n", dev_err(arizona->dev, "Removal report failed: %d\n",
ret); ret);
...@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
dev_err(&pdev->dev, "Failed to allocate memory\n"); dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM; return -ENOMEM;
goto err;
} }
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD"); info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
if (IS_ERR(info->micvdd)) { if (IS_ERR(info->micvdd)) {
ret = PTR_ERR(info->micvdd); ret = PTR_ERR(info->micvdd);
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret); dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
goto err; return ret;
} }
mutex_init(&info->lock); mutex_init(&info->lock);
...@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break; break;
} }
info->edev.name = "Headset Jack"; info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
info->edev.dev.parent = arizona->dev; if (IS_ERR(info->edev)) {
info->edev.supported_cable = arizona_cable; dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
info->edev->name = "Headset Jack";
info->edev->dev.parent = arizona->dev;
ret = extcon_dev_register(&info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret < 0) { if (ret < 0) {
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n", dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret); ret);
goto err; return ret;
} }
info->input = devm_input_allocate_device(&pdev->dev); info->input = devm_input_allocate_device(&pdev->dev);
...@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) ...@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
err_input: err_input:
err_register: err_register:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
err:
return ret; return ret;
} }
...@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev) ...@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0); ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona); arizona_clk32k_disable(arizona);
extcon_dev_unregister(&info->edev);
return 0; return 0;
} }
......
...@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev) ...@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
{ {
} }
/*
* extcon_dev_allocate() - Allocate the memory of extcon device.
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function allocates the memory for extcon device without allocating
* memory in each extcon provider driver and initialize default setting for
* extcon device.
*
* Return the pointer of extcon device if success or ERR_PTR(err) if fail
*/
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
{
struct extcon_dev *edev;
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
if (!edev)
return ERR_PTR(-ENOMEM);
edev->max_supported = 0;
edev->supported_cable = supported_cable;
return edev;
}
/*
* extcon_dev_free() - Free the memory of extcon device.
* @edev: the extcon device to free
*/
void extcon_dev_free(struct extcon_dev *edev)
{
kfree(edev);
}
EXPORT_SYMBOL_GPL(extcon_dev_free);
static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
{
struct extcon_dev **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
static void devm_extcon_dev_release(struct device *dev, void *res)
{
extcon_dev_free(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_allocate - Allocate managed extcon device
* @dev: device owning the extcon device being created
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function manages automatically the memory of extcon device using device
* resource management and simplify the control of freeing the memory of extcon
* device.
*
* Returns the pointer memory of allocated extcon_dev if success
* or ERR_PTR(err) if fail
*/
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **supported_cable)
{
struct extcon_dev **ptr, *edev;
ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
edev = extcon_dev_allocate(supported_cable);
if (IS_ERR(edev)) {
devres_free(ptr);
return edev;
}
*ptr = edev;
devres_add(dev, ptr);
return edev;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_release,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
/** /**
* extcon_dev_register() - Register a new extcon device * extcon_dev_register() - Register a new extcon device
* @edev : the new extcon device (should be allocated before calling) * @edev : the new extcon device (should be allocated before calling)
...@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev) ...@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
} }
EXPORT_SYMBOL_GPL(extcon_dev_unregister); EXPORT_SYMBOL_GPL(extcon_dev_unregister);
static void devm_extcon_dev_unreg(struct device *dev, void *res)
{
extcon_dev_unregister(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_register() - Resource-managed extcon_dev_register()
* @dev: device to allocate extcon device
* @edev: the new extcon device to register
*
* Managed extcon_dev_register() function. If extcon device is attached with
* this function, that extcon device is automatically unregistered on driver
* detach. Internally this function calls extcon_dev_register() function.
* To get more information, refer that function.
*
* If extcon device is registered with this function and the device needs to be
* unregistered separately, devm_extcon_dev_unregister() should be used.
*
* Returns 0 if success or negaive error number if failure.
*/
int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
{
struct extcon_dev **ptr;
int ret;
ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = extcon_dev_register(edev);
if (ret) {
devres_free(ptr);
return ret;
}
*ptr = edev;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
/**
* devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
* @dev: device the extcon belongs to
* @edev: the extcon device to unregister
*
* Unregister extcon device that is registered with devm_extcon_dev_register()
* function.
*/
void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
#ifdef CONFIG_OF #ifdef CONFIG_OF
/* /*
* extcon_get_edev_by_phandle - Get the extcon device from devicetree * extcon_get_edev_by_phandle - Get the extcon device from devicetree
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include <linux/extcon/extcon-gpio.h> #include <linux/extcon/extcon-gpio.h>
struct gpio_extcon_data { struct gpio_extcon_data {
struct extcon_dev edev; struct extcon_dev *edev;
unsigned gpio; unsigned gpio;
bool gpio_active_low; bool gpio_active_low;
const char *state_on; const char *state_on;
...@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work) ...@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
state = gpio_get_value(data->gpio); state = gpio_get_value(data->gpio);
if (data->gpio_active_low) if (data->gpio_active_low)
state = !state; state = !state;
extcon_set_state(&data->edev, state); extcon_set_state(data->edev, state);
} }
static irqreturn_t gpio_irq_handler(int irq, void *dev_id) static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
...@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) ...@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf) static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
{ {
struct gpio_extcon_data *extcon_data = struct device *dev = edev->dev.parent;
container_of(edev, struct gpio_extcon_data, edev); struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
const char *state; const char *state;
if (extcon_get_state(edev)) if (extcon_get_state(edev))
state = extcon_data->state_on; state = extcon_data->state_on;
else else
...@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev) ...@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
if (!extcon_data) if (!extcon_data)
return -ENOMEM; return -ENOMEM;
extcon_data->edev.name = pdata->name; extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
extcon_data->edev.dev.parent = &pdev->dev; if (IS_ERR(extcon_data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
extcon_data->edev->name = pdata->name;
extcon_data->edev->dev.parent = &pdev->dev;
extcon_data->gpio = pdata->gpio; extcon_data->gpio = pdata->gpio;
extcon_data->gpio_active_low = pdata->gpio_active_low; extcon_data->gpio_active_low = pdata->gpio_active_low;
extcon_data->state_on = pdata->state_on; extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off; extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume; extcon_data->check_on_resume = pdata->check_on_resume;
if (pdata->state_on && pdata->state_off) if (pdata->state_on && pdata->state_off)
extcon_data->edev.print_state = extcon_gpio_print_state; extcon_data->edev->print_state = extcon_gpio_print_state;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name); pdev->name);
...@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev) ...@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
msecs_to_jiffies(pdata->debounce); msecs_to_jiffies(pdata->debounce);
} }
ret = extcon_dev_register(&extcon_data->edev); ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
if (ret < 0) if (ret < 0)
return ret; return ret;
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work); INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
extcon_data->irq = gpio_to_irq(extcon_data->gpio); extcon_data->irq = gpio_to_irq(extcon_data->gpio);
if (extcon_data->irq < 0) { if (extcon_data->irq < 0)
ret = extcon_data->irq; return extcon_data->irq;
goto err;
}
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler, ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
pdata->irq_flags, pdev->name, pdata->irq_flags, pdev->name,
extcon_data); extcon_data);
if (ret < 0) if (ret < 0)
goto err; return ret;
platform_set_drvdata(pdev, extcon_data); platform_set_drvdata(pdev, extcon_data);
/* Perform initial detection */ /* Perform initial detection */
gpio_extcon_work(&extcon_data->work.work); gpio_extcon_work(&extcon_data->work.work);
return 0; return 0;
err:
extcon_dev_unregister(&extcon_data->edev);
return ret;
} }
static int gpio_extcon_remove(struct platform_device *pdev) static int gpio_extcon_remove(struct platform_device *pdev)
...@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev) ...@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&extcon_data->work); cancel_delayed_work_sync(&extcon_data->work);
free_irq(extcon_data->irq, extcon_data); free_irq(extcon_data->irq, extcon_data);
extcon_dev_unregister(&extcon_data->edev);
return 0; return 0;
} }
......
/* /*
* extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2013,2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -24,7 +25,6 @@ ...@@ -24,7 +25,6 @@
#include <linux/mfd/max14577-private.h> #include <linux/mfd/max14577-private.h>
#include <linux/extcon.h> #include <linux/extcon.h>
#define DEV_NAME "max14577-muic"
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
enum max14577_muic_adc_debounce_time { enum max14577_muic_adc_debounce_time {
...@@ -40,6 +40,42 @@ enum max14577_muic_status { ...@@ -40,6 +40,42 @@ enum max14577_muic_status {
MAX14577_MUIC_STATUS_END, MAX14577_MUIC_STATUS_END,
}; };
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq max14577_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
static struct max14577_muic_irq max77836_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
{ MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" },
};
struct max14577_muic_info { struct max14577_muic_info {
struct device *dev; struct device *dev;
struct max14577 *max14577; struct max14577 *max14577;
...@@ -48,6 +84,8 @@ struct max14577_muic_info { ...@@ -48,6 +84,8 @@ struct max14577_muic_info {
int prev_chg_type; int prev_chg_type;
u8 status[MAX14577_MUIC_STATUS_END]; u8 status[MAX14577_MUIC_STATUS_END];
struct max14577_muic_irq *muic_irqs;
unsigned int muic_irqs_num;
bool irq_adc; bool irq_adc;
bool irq_chg; bool irq_chg;
struct work_struct irq_work; struct work_struct irq_work;
...@@ -74,29 +112,6 @@ enum max14577_muic_cable_group { ...@@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
MAX14577_CABLE_GROUP_CHG, MAX14577_CABLE_GROUP_CHG,
}; };
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
/* Define supported accessory type */ /* Define supported accessory type */
enum max14577_muic_acc_type { enum max14577_muic_acc_type {
MAX14577_MUIC_ADC_GROUND = 0x0, MAX14577_MUIC_ADC_GROUND = 0x0,
...@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work) ...@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
return; return;
} }
static irqreturn_t max14577_muic_irq_handler(int irq, void *data) /*
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
* Returns 0 if irq_type does not match registered IRQ for this device type.
*/
static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
{ {
struct max14577_muic_info *info = data;
int i, irq_type = -1;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
if (irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
switch (irq_type) { switch (irq_type) {
case MAX14577_IRQ_INT1_ADC: case MAX14577_IRQ_INT1_ADC:
case MAX14577_IRQ_INT1_ADCLOW: case MAX14577_IRQ_INT1_ADCLOW:
...@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) ...@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
/* Handle all of accessory except for /* Handle all of accessory except for
type of charger accessory */ type of charger accessory */
info->irq_adc = true; info->irq_adc = true;
break; return 1;
case MAX14577_IRQ_INT2_CHGTYP: case MAX14577_IRQ_INT2_CHGTYP:
case MAX14577_IRQ_INT2_CHGDETRUN: case MAX14577_IRQ_INT2_CHGDETRUN:
case MAX14577_IRQ_INT2_DCDTMR: case MAX14577_IRQ_INT2_DCDTMR:
...@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data) ...@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
case MAX14577_IRQ_INT2_VBVOLT: case MAX14577_IRQ_INT2_VBVOLT:
/* Handle charger accessory */ /* Handle charger accessory */
info->irq_chg = true; info->irq_chg = true;
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; break;
case MAXIM_DEVICE_TYPE_MAX14577:
default: default:
irq_parsed = max14577_parse_irq(info, irq_type);
break;
}
if (!irq_parsed) {
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n", dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
irq_type); irq_type);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev)
INIT_WORK(&info->irq_work, max14577_muic_irq_work); INIT_WORK(&info->irq_work, max14577_muic_irq_work);
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
info->muic_irqs = max77836_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
info->muic_irqs = max14577_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs);
}
/* Support irq domain for max14577 MUIC device */ /* Support irq domain for max14577 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) { for (i = 0; i < info->muic_irqs_num; i++) {
struct max14577_muic_irq *muic_irq = &muic_irqs[i]; struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
unsigned int virq = 0; unsigned int virq = 0;
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq); virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
if (!virq) if (virq <= 0)
return -EINVAL; return -EINVAL;
muic_irq->virq = virq; muic_irq->virq = virq;
...@@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
} }
/* Initialize extcon device */ /* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL); info->edev = devm_extcon_dev_allocate(&pdev->dev,
if (!info->edev) { max14577_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM; return -ENOMEM;
} }
info->edev->name = DEV_NAME;
info->edev->supported_cable = max14577_extcon_cable; info->edev->name = dev_name(&pdev->dev);
ret = extcon_dev_register(info->edev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
return ret; return ret;
...@@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
MAX14577_REG_DEVICEID, &id); MAX14577_REG_DEVICEID, &id);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n"); dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon; return ret;
} }
dev_info(info->dev, "device ID : 0x%x\n", id); dev_info(info->dev, "device ID : 0x%x\n", id);
...@@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev) ...@@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies); delay_jiffies);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to schedule delayed work for cable detect\n");
goto err_extcon;
}
return ret; return ret;
err_extcon:
extcon_dev_unregister(info->edev);
return ret;
} }
static int max14577_muic_remove(struct platform_device *pdev) static int max14577_muic_remove(struct platform_device *pdev)
...@@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev) ...@@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev)
struct max14577_muic_info *info = platform_get_drvdata(pdev); struct max14577_muic_info *info = platform_get_drvdata(pdev);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
static const struct platform_device_id max14577_muic_id[] = {
{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_muic_id);
static struct platform_driver max14577_muic_driver = { static struct platform_driver max14577_muic_driver = {
.driver = { .driver = {
.name = DEV_NAME, .name = "max14577-muic",
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = max14577_muic_probe, .probe = max14577_muic_probe,
.remove = max14577_muic_remove, .remove = max14577_muic_remove,
.id_table = max14577_muic_id,
}; };
module_platform_driver(max14577_muic_driver); module_platform_driver(max14577_muic_driver);
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver"); MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:extcon-max14577"); MODULE_ALIAS("platform:extcon-max14577");
...@@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)
} }
/* Initialize extcon device */ /* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), info->edev = devm_extcon_dev_allocate(&pdev->dev,
GFP_KERNEL); max77693_extcon_cable);
if (!info->edev) { if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_irq; goto err_irq;
} }
info->edev->name = DEV_NAME; info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev; info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max77693_extcon_cable;
ret = extcon_dev_register(info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq; goto err_irq;
} }
/* Initialize MUIC register by using platform data or default data */ /* Initialize MUIC register by using platform data or default data */
if (pdata->muic_data) { if (pdata && pdata->muic_data) {
init_data = pdata->muic_data->init_data; init_data = pdata->muic_data->init_data;
num_init_data = pdata->muic_data->num_init_data; num_init_data = pdata->muic_data->num_init_data;
} else { } else {
...@@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
= init_data[i].data; = init_data[i].data;
} }
if (pdata->muic_data) { if (pdata && pdata->muic_data) {
struct max77693_muic_platform_data *muic_pdata struct max77693_muic_platform_data *muic_pdata
= pdata->muic_data; = pdata->muic_data;
...@@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
MAX77693_MUIC_REG_ID, &id); MAX77693_MUIC_REG_ID, &id);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n"); dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon; goto err_irq;
} }
dev_info(info->dev, "device ID : 0x%x\n", id); dev_info(info->dev, "device ID : 0x%x\n", id);
...@@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev) ...@@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies); queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return ret; return ret;
err_extcon:
extcon_dev_unregister(info->edev);
err_irq: err_irq:
while (--i >= 0) while (--i >= 0)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
...@@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev) ...@@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
input_unregister_device(info->dock); input_unregister_device(info->dock);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
......
...@@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev) ...@@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)
} }
/* External connector */ /* External connector */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev), info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
GFP_KERNEL); if (IS_ERR(info->edev)) {
if (!info->edev) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_irq; goto err_irq;
} }
info->edev->name = DEV_NAME; info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev; info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max8997_extcon_cable;
ret = extcon_dev_register(info->edev); ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq; goto err_irq;
} }
if (pdata->muic_pdata) { if (pdata && pdata->muic_pdata) {
struct max8997_muic_platform_data *muic_pdata struct max8997_muic_platform_data *muic_pdata
= pdata->muic_pdata; = pdata->muic_pdata;
...@@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev) ...@@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer. * driver should notify cable state to upper layer.
*/ */
INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq); INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies); queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return 0; return 0;
...@@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev) ...@@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info); free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work); cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0; return 0;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mfd/palmas.h> #include <linux/mfd/palmas.h>
#include <linux/of.h> #include <linux/of.h>
...@@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) ...@@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_cable_state(&palmas_usb->edev, "USB", true); extcon_set_cable_state(palmas_usb->edev, "USB", true);
dev_info(palmas_usb->dev, "USB cable is attached\n"); dev_info(palmas_usb->dev, "USB cable is attached\n");
} else { } else {
dev_dbg(palmas_usb->dev, dev_dbg(palmas_usb->dev,
...@@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) ...@@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB", false); extcon_set_cable_state(palmas_usb->edev, "USB", false);
dev_info(palmas_usb->dev, "USB cable is detached\n"); dev_info(palmas_usb->dev, "USB cable is detached\n");
} else { } else {
dev_dbg(palmas_usb->dev, dev_dbg(palmas_usb->dev,
...@@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) ...@@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID; palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
...@@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) ...@@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR, PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID; palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true); extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n"); dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
} }
...@@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, palmas_usb); platform_set_drvdata(pdev, palmas_usb);
palmas_usb->edev.supported_cable = palmas_extcon_cable; palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
palmas_usb->edev.dev.parent = palmas_usb->dev; palmas_extcon_cable);
palmas_usb->edev.mutually_exclusive = mutually_exclusive; if (IS_ERR(palmas_usb->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
palmas_usb->edev->dev.parent = palmas_usb->dev;
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
status = extcon_dev_register(&palmas_usb->edev); status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
if (status) { if (status) {
dev_err(&pdev->dev, "failed to register extcon device\n"); dev_err(&pdev->dev, "failed to register extcon device\n");
kfree(palmas_usb->edev->name);
return status; return status;
} }
...@@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->id_irq, status); palmas_usb->id_irq, status);
goto fail_extcon; kfree(palmas_usb->edev->name);
return status;
} }
} }
...@@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev) ...@@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) { if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->vbus_irq, status); palmas_usb->vbus_irq, status);
goto fail_extcon; kfree(palmas_usb->edev->name);
return status;
} }
} }
palmas_enable_irq(palmas_usb); palmas_enable_irq(palmas_usb);
device_set_wakeup_capable(&pdev->dev, true); device_set_wakeup_capable(&pdev->dev, true);
return 0; return 0;
fail_extcon:
extcon_dev_unregister(&palmas_usb->edev);
return status;
} }
static int palmas_usb_remove(struct platform_device *pdev) static int palmas_usb_remove(struct platform_device *pdev)
{ {
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
extcon_dev_unregister(&palmas_usb->edev); kfree(palmas_usb->edev->name);
return 0; return 0;
} }
......
...@@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) ...@@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
} }
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
static void reset_channel_cb(void *arg)
{
struct vmbus_channel *channel = arg;
channel->onchannel_callback = NULL;
}
static void vmbus_close_internal(struct vmbus_channel *channel) static void vmbus_close_internal(struct vmbus_channel *channel)
{ {
struct vmbus_channel_close_channel *msg; struct vmbus_channel_close_channel *msg;
int ret; int ret;
unsigned long flags;
channel->state = CHANNEL_OPEN_STATE; channel->state = CHANNEL_OPEN_STATE;
channel->sc_creation_callback = NULL; channel->sc_creation_callback = NULL;
/* Stop callback and cancel the timer asap */ /* Stop callback and cancel the timer asap */
spin_lock_irqsave(&channel->inbound_lock, flags); if (channel->target_cpu != smp_processor_id())
channel->onchannel_callback = NULL; smp_call_function_single(channel->target_cpu, reset_channel_cb,
spin_unlock_irqrestore(&channel->inbound_lock, flags); channel, true);
else
reset_channel_cb(channel);
/* Send a closing message */ /* Send a closing message */
...@@ -674,8 +682,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, ...@@ -674,8 +682,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
multi_pagebuffer->len); multi_pagebuffer->len);
if (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT))
return -EINVAL; return -EINVAL;
/* /*
......
...@@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void) ...@@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void)
spin_lock_init(&channel->sc_lock); spin_lock_init(&channel->sc_lock);
INIT_LIST_HEAD(&channel->sc_list); INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
channel->controlwq = create_workqueue("hv_vmbus_ctl"); channel->controlwq = create_workqueue("hv_vmbus_ctl");
if (!channel->controlwq) { if (!channel->controlwq) {
...@@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel) ...@@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel)
queue_work(vmbus_connection.work_queue, &channel->work); queue_work(vmbus_connection.work_queue, &channel->work);
} }
static void percpu_channel_enq(void *arg)
{
struct vmbus_channel *channel = arg;
int cpu = smp_processor_id();
list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]);
}
static void percpu_channel_deq(void *arg)
{
struct vmbus_channel *channel = arg;
list_del(&channel->percpu_list);
}
/* /*
* vmbus_process_rescind_offer - * vmbus_process_rescind_offer -
...@@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work) ...@@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
msg.header.msgtype = CHANNELMSG_RELID_RELEASED; msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
if (channel->target_cpu != smp_processor_id())
smp_call_function_single(channel->target_cpu,
percpu_channel_deq, channel, true);
else
percpu_channel_deq(channel);
if (channel->primary_channel == NULL) { if (channel->primary_channel == NULL) {
spin_lock_irqsave(&vmbus_connection.channel_lock, flags); spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_del(&channel->listentry); list_del(&channel->listentry);
...@@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work) ...@@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work)
work); work);
struct vmbus_channel *channel; struct vmbus_channel *channel;
bool fnew = true; bool fnew = true;
bool enq = false;
int ret; int ret;
unsigned long flags; unsigned long flags;
...@@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work) ...@@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work)
} }
} }
if (fnew) if (fnew) {
list_add_tail(&newchannel->listentry, list_add_tail(&newchannel->listentry,
&vmbus_connection.chn_list); &vmbus_connection.chn_list);
enq = true;
}
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
if (enq) {
if (newchannel->target_cpu != smp_processor_id())
smp_call_function_single(newchannel->target_cpu,
percpu_channel_enq,
newchannel, true);
else
percpu_channel_enq(newchannel);
}
if (!fnew) { if (!fnew) {
/* /*
* Check to see if this is a sub-channel. * Check to see if this is a sub-channel.
...@@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work) ...@@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work)
spin_lock_irqsave(&channel->sc_lock, flags); spin_lock_irqsave(&channel->sc_lock, flags);
list_add_tail(&newchannel->sc_list, &channel->sc_list); list_add_tail(&newchannel->sc_list, &channel->sc_list);
spin_unlock_irqrestore(&channel->sc_lock, flags); spin_unlock_irqrestore(&channel->sc_lock, flags);
if (newchannel->target_cpu != smp_processor_id())
smp_call_function_single(newchannel->target_cpu,
percpu_channel_enq,
newchannel, true);
else
percpu_channel_enq(newchannel);
newchannel->state = CHANNEL_OPEN_STATE; newchannel->state = CHANNEL_OPEN_STATE;
if (channel->sc_creation_callback != NULL) if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel); channel->sc_creation_callback(newchannel);
...@@ -365,7 +404,7 @@ static u32 next_vp; ...@@ -365,7 +404,7 @@ static u32 next_vp;
* performance critical channels (IDE, SCSI and Network) will be uniformly * performance critical channels (IDE, SCSI and Network) will be uniformly
* distributed across all available CPUs. * distributed across all available CPUs.
*/ */
static u32 get_vp_index(uuid_le *type_guid) static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid)
{ {
u32 cur_cpu; u32 cur_cpu;
int i; int i;
...@@ -387,10 +426,13 @@ static u32 get_vp_index(uuid_le *type_guid) ...@@ -387,10 +426,13 @@ static u32 get_vp_index(uuid_le *type_guid)
* Also if the channel is not a performance critical * Also if the channel is not a performance critical
* channel, bind it to cpu 0. * channel, bind it to cpu 0.
*/ */
return 0; channel->target_cpu = 0;
channel->target_vp = 0;
return;
} }
cur_cpu = (++next_vp % max_cpus); cur_cpu = (++next_vp % max_cpus);
return hv_context.vp_index[cur_cpu]; channel->target_cpu = cur_cpu;
channel->target_vp = hv_context.vp_index[cur_cpu];
} }
/* /*
...@@ -438,7 +480,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) ...@@ -438,7 +480,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
offer->connection_id; offer->connection_id;
} }
newchannel->target_vp = get_vp_index(&offer->offer.if_type); init_vp_index(newchannel, &offer->offer.if_type);
memcpy(&newchannel->offermsg, offer, memcpy(&newchannel->offermsg, offer,
sizeof(struct vmbus_channel_offer_channel)); sizeof(struct vmbus_channel_offer_channel));
......
...@@ -224,8 +224,8 @@ int vmbus_connect(void) ...@@ -224,8 +224,8 @@ int vmbus_connect(void)
vmbus_connection.int_page = NULL; vmbus_connection.int_page = NULL;
} }
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1); free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0);
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1); free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
vmbus_connection.monitor_pages[0] = NULL; vmbus_connection.monitor_pages[0] = NULL;
vmbus_connection.monitor_pages[1] = NULL; vmbus_connection.monitor_pages[1] = NULL;
...@@ -234,6 +234,28 @@ int vmbus_connect(void) ...@@ -234,6 +234,28 @@ int vmbus_connect(void)
return ret; return ret;
} }
/*
* Map the given relid to the corresponding channel based on the
* per-cpu list of channels that have been affinitized to this CPU.
* This will be used in the channel callback path as we can do this
* mapping in a lock-free fashion.
*/
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
{
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
int cpu = smp_processor_id();
struct list_head *pcpu_head = &hv_context.percpu_list[cpu];
list_for_each_entry(channel, pcpu_head, percpu_list) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
}
}
return found_channel;
}
/* /*
* relid2channel - Get the channel object given its * relid2channel - Get the channel object given its
...@@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid) ...@@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid)
static void process_chn_event(u32 relid) static void process_chn_event(u32 relid)
{ {
struct vmbus_channel *channel; struct vmbus_channel *channel;
unsigned long flags;
void *arg; void *arg;
bool read_state; bool read_state;
u32 bytes_to_read; u32 bytes_to_read;
...@@ -286,7 +307,7 @@ static void process_chn_event(u32 relid) ...@@ -286,7 +307,7 @@ static void process_chn_event(u32 relid)
* Find the channel based on this relid and invokes the * Find the channel based on this relid and invokes the
* channel callback to process the event * channel callback to process the event
*/ */
channel = relid2channel(relid); channel = pcpu_relid2channel(relid);
if (!channel) { if (!channel) {
pr_err("channel not found for relid - %u\n", relid); pr_err("channel not found for relid - %u\n", relid);
...@@ -296,13 +317,12 @@ static void process_chn_event(u32 relid) ...@@ -296,13 +317,12 @@ static void process_chn_event(u32 relid)
/* /*
* A channel once created is persistent even when there * A channel once created is persistent even when there
* is no driver handling the device. An unloading driver * is no driver handling the device. An unloading driver
* sets the onchannel_callback to NULL under the * sets the onchannel_callback to NULL on the same CPU
* protection of the channel inbound_lock. Thus, checking * as where this interrupt is handled (in an interrupt context).
* and invoking the driver specific callback takes care of * Thus, checking and invoking the driver specific callback takes
* orderly unloading of the driver. * care of orderly unloading of the driver.
*/ */
spin_lock_irqsave(&channel->inbound_lock, flags);
if (channel->onchannel_callback != NULL) { if (channel->onchannel_callback != NULL) {
arg = channel->channel_callback_context; arg = channel->channel_callback_context;
read_state = channel->batched_reading; read_state = channel->batched_reading;
...@@ -327,7 +347,6 @@ static void process_chn_event(u32 relid) ...@@ -327,7 +347,6 @@ static void process_chn_event(u32 relid)
pr_err("no channel callback for relid - %u\n", relid); pr_err("no channel callback for relid - %u\n", relid);
} }
spin_unlock_irqrestore(&channel->inbound_lock, flags);
} }
/* /*
......
...@@ -383,6 +383,8 @@ void hv_synic_init(void *arg) ...@@ -383,6 +383,8 @@ void hv_synic_init(void *arg)
*/ */
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index); rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_context.vp_index[cpu] = (u32)vp_index; hv_context.vp_index[cpu] = (u32)vp_index;
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
return; return;
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -459,6 +460,11 @@ static bool do_hot_add; ...@@ -459,6 +460,11 @@ static bool do_hot_add;
*/ */
static uint pressure_report_delay = 45; static uint pressure_report_delay = 45;
/*
* The last time we posted a pressure report to host.
*/
static unsigned long last_post_time;
module_param(hot_add, bool, (S_IRUGO | S_IWUSR)); module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add"); MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
...@@ -542,6 +548,7 @@ struct hv_dynmem_device { ...@@ -542,6 +548,7 @@ struct hv_dynmem_device {
static struct hv_dynmem_device dm_device; static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size) static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
...@@ -612,7 +619,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, ...@@ -612,7 +619,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
* have not been "onlined" within the allowed time. * have not been "onlined" within the allowed time.
*/ */
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ); wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
post_status(&dm_device);
} }
return; return;
...@@ -951,11 +958,17 @@ static void post_status(struct hv_dynmem_device *dm) ...@@ -951,11 +958,17 @@ static void post_status(struct hv_dynmem_device *dm)
{ {
struct dm_status status; struct dm_status status;
struct sysinfo val; struct sysinfo val;
unsigned long now = jiffies;
unsigned long last_post = last_post_time;
if (pressure_report_delay > 0) { if (pressure_report_delay > 0) {
--pressure_report_delay; --pressure_report_delay;
return; return;
} }
if (!time_after(now, (last_post_time + HZ)))
return;
si_meminfo(&val); si_meminfo(&val);
memset(&status, 0, sizeof(struct dm_status)); memset(&status, 0, sizeof(struct dm_status));
status.hdr.type = DM_STATUS_REPORT; status.hdr.type = DM_STATUS_REPORT;
...@@ -983,6 +996,14 @@ static void post_status(struct hv_dynmem_device *dm) ...@@ -983,6 +996,14 @@ static void post_status(struct hv_dynmem_device *dm)
if (status.hdr.trans_id != atomic_read(&trans_id)) if (status.hdr.trans_id != atomic_read(&trans_id))
return; return;
/*
* If the last post time that we sampled has changed,
* we have raced, don't post the status.
*/
if (last_post != last_post_time)
return;
last_post_time = jiffies;
vmbus_sendpacket(dm->dev->channel, &status, vmbus_sendpacket(dm->dev->channel, &status,
sizeof(struct dm_status), sizeof(struct dm_status),
(unsigned long)NULL, (unsigned long)NULL,
...@@ -1117,7 +1138,7 @@ static void balloon_up(struct work_struct *dummy) ...@@ -1117,7 +1138,7 @@ static void balloon_up(struct work_struct *dummy)
if (ret == -EAGAIN) if (ret == -EAGAIN)
msleep(20); msleep(20);
post_status(&dm_device);
} while (ret == -EAGAIN); } while (ret == -EAGAIN);
if (ret) { if (ret) {
...@@ -1144,8 +1165,10 @@ static void balloon_down(struct hv_dynmem_device *dm, ...@@ -1144,8 +1165,10 @@ static void balloon_down(struct hv_dynmem_device *dm,
struct dm_unballoon_response resp; struct dm_unballoon_response resp;
int i; int i;
for (i = 0; i < range_count; i++) for (i = 0; i < range_count; i++) {
free_balloon_pages(dm, &range_array[i]); free_balloon_pages(dm, &range_array[i]);
post_status(&dm_device);
}
if (req->more_pages == 1) if (req->more_pages == 1)
return; return;
......
...@@ -510,6 +510,11 @@ struct hv_context { ...@@ -510,6 +510,11 @@ struct hv_context {
* basis. * basis.
*/ */
struct tasklet_struct *event_dpc[NR_CPUS]; struct tasklet_struct *event_dpc[NR_CPUS];
/*
* To optimize the mapping of relid to channel, maintain
* per-cpu list of the channels based on their CPU affinity.
*/
struct list_head percpu_list[NR_CPUS];
}; };
extern struct hv_context hv_context; extern struct hv_context hv_context;
......
...@@ -183,14 +183,14 @@ EXPORT_SYMBOL_GPL(mcb_device_register); ...@@ -183,14 +183,14 @@ EXPORT_SYMBOL_GPL(mcb_device_register);
* *
* Allocate a new @mcb_bus. * Allocate a new @mcb_bus.
*/ */
struct mcb_bus *mcb_alloc_bus(void) struct mcb_bus *mcb_alloc_bus(struct device *carrier)
{ {
struct mcb_bus *bus; struct mcb_bus *bus;
int bus_nr; int bus_nr;
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL); bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
if (!bus) if (!bus)
return NULL; return ERR_PTR(-ENOMEM);
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL); bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
if (bus_nr < 0) { if (bus_nr < 0) {
...@@ -200,7 +200,7 @@ struct mcb_bus *mcb_alloc_bus(void) ...@@ -200,7 +200,7 @@ struct mcb_bus *mcb_alloc_bus(void)
INIT_LIST_HEAD(&bus->children); INIT_LIST_HEAD(&bus->children);
bus->bus_nr = bus_nr; bus->bus_nr = bus_nr;
bus->carrier = carrier;
return bus; return bus;
} }
EXPORT_SYMBOL_GPL(mcb_alloc_bus); EXPORT_SYMBOL_GPL(mcb_alloc_bus);
...@@ -378,6 +378,13 @@ void mcb_release_mem(struct resource *mem) ...@@ -378,6 +378,13 @@ void mcb_release_mem(struct resource *mem)
} }
EXPORT_SYMBOL_GPL(mcb_release_mem); EXPORT_SYMBOL_GPL(mcb_release_mem);
static int __mcb_get_irq(struct mcb_device *dev)
{
struct resource *irq = &dev->irq;
return irq->start;
}
/** /**
* mcb_get_irq() - Get device's IRQ number * mcb_get_irq() - Get device's IRQ number
* @dev: The @mcb_device the IRQ is for * @dev: The @mcb_device the IRQ is for
...@@ -386,9 +393,12 @@ EXPORT_SYMBOL_GPL(mcb_release_mem); ...@@ -386,9 +393,12 @@ EXPORT_SYMBOL_GPL(mcb_release_mem);
*/ */
int mcb_get_irq(struct mcb_device *dev) int mcb_get_irq(struct mcb_device *dev)
{ {
struct resource *irq = &dev->irq; struct mcb_bus *bus = dev->bus;
return irq->start; if (bus->get_irq)
return bus->get_irq(dev);
return __mcb_get_irq(dev);
} }
EXPORT_SYMBOL_GPL(mcb_get_irq); EXPORT_SYMBOL_GPL(mcb_get_irq);
......
...@@ -20,6 +20,15 @@ struct priv { ...@@ -20,6 +20,15 @@ struct priv {
void __iomem *base; void __iomem *base;
}; };
static int mcb_pci_get_irq(struct mcb_device *mdev)
{
struct mcb_bus *mbus = mdev->bus;
struct device *dev = mbus->carrier;
struct pci_dev *pdev = to_pci_dev(dev);
return pdev->irq;
}
static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct priv *priv; struct priv *priv;
...@@ -67,7 +76,13 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -67,7 +76,13 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, priv); pci_set_drvdata(pdev, priv);
priv->bus = mcb_alloc_bus(); priv->bus = mcb_alloc_bus(&pdev->dev);
if (IS_ERR(priv->bus)) {
ret = PTR_ERR(priv->bus);
goto err_drvdata;
}
priv->bus->get_irq = mcb_pci_get_irq;
ret = chameleon_parse_cells(priv->bus, mapbase, priv->base); ret = chameleon_parse_cells(priv->bus, mapbase, priv->base);
if (ret < 0) if (ret < 0)
......
...@@ -331,15 +331,15 @@ config MFD_88PM860X ...@@ -331,15 +331,15 @@ config MFD_88PM860X
battery-charger under the corresponding menus. battery-charger under the corresponding menus.
config MFD_MAX14577 config MFD_MAX14577
bool "Maxim Semiconductor MAX14577 MUIC + Charger Support" bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C=y depends on I2C=y
select MFD_CORE select MFD_CORE
select REGMAP_I2C select REGMAP_I2C
select REGMAP_IRQ select REGMAP_IRQ
select IRQ_DOMAIN select IRQ_DOMAIN
help help
Say yes here to add support for Maxim Semiconductor MAX14577. Say yes here to add support for Maxim Semiconductor MAX14577 and
This is a Micro-USB IC with Charger controls on chip. MAX77836 Micro-USB ICs with battery charger.
This driver provides common support for accessing the device; This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality additional drivers must be enabled in order to use the functionality
of the device. of the device.
......
This diff is collapsed.
...@@ -54,6 +54,7 @@ config AD525X_DPOT_SPI ...@@ -54,6 +54,7 @@ config AD525X_DPOT_SPI
config ATMEL_PWM config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support" tristate "Atmel AT32/AT91 PWM support"
depends on HAVE_CLK depends on HAVE_CLK
depends on AVR32 || AT91SAM9263 || AT91SAM9RL || AT91SAM9G45
help help
This option enables device driver support for the PWM channels This option enables device driver support for the PWM channels
on certain Atmel processors. Pulse Width Modulation is used for on certain Atmel processors. Pulse Width Modulation is used for
...@@ -200,7 +201,7 @@ config ICS932S401 ...@@ -200,7 +201,7 @@ config ICS932S401
config ATMEL_SSC config ATMEL_SSC
tristate "Device driver for Atmel SSC peripheral" tristate "Device driver for Atmel SSC peripheral"
depends on HAS_IOMEM depends on HAS_IOMEM && (AVR32 || ARCH_AT91 || COMPILE_TEST)
---help--- ---help---
This option enables device driver support for Atmel Synchronized This option enables device driver support for Atmel Synchronized
Serial Communication peripheral (SSC). Serial Communication peripheral (SSC).
...@@ -468,7 +469,7 @@ config BMP085_SPI ...@@ -468,7 +469,7 @@ config BMP085_SPI
config PCH_PHUB config PCH_PHUB
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
select GENERIC_NET_UTILS select GENERIC_NET_UTILS
depends on PCI depends on PCI && (X86_32 || COMPILE_TEST)
help help
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -366,11 +367,17 @@ static const struct dev_pm_ops charlcd_pm_ops = { ...@@ -366,11 +367,17 @@ static const struct dev_pm_ops charlcd_pm_ops = {
.resume = charlcd_resume, .resume = charlcd_resume,
}; };
static const struct of_device_id charlcd_match[] = {
{ .compatible = "arm,versatile-lcd", },
{}
};
static struct platform_driver charlcd_driver = { static struct platform_driver charlcd_driver = {
.driver = { .driver = {
.name = DRIVERNAME, .name = DRIVERNAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &charlcd_pm_ops, .pm = &charlcd_pm_ops,
.of_match_table = of_match_ptr(charlcd_match),
}, },
.remove = __exit_p(charlcd_remove), .remove = __exit_p(charlcd_remove),
}; };
......
...@@ -85,7 +85,6 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr, ...@@ -85,7 +85,6 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
{ {
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
char *endp;
u64 val; u64 val;
__le32 val_le; __le32 val_le;
int rc; int rc;
...@@ -93,8 +92,8 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr, ...@@ -93,8 +92,8 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name); dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
/* Decode input */ /* Decode input */
val = simple_strtoull(buf, &endp, 0); rc = kstrtoull(buf, 0, &val);
if (buf == endp) { if (rc < 0) {
dev_dbg(dev, "input string not a number\n"); dev_dbg(dev, "input string not a number\n");
return -EINVAL; return -EINVAL;
} }
......
...@@ -348,7 +348,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd) ...@@ -348,7 +348,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
char name[64]; char name[64];
unsigned int i; unsigned int i;
sprintf(card_name, "%s%u_card", GENWQE_DEVNAME, cd->card_idx); sprintf(card_name, "%s%d_card", GENWQE_DEVNAME, cd->card_idx);
root = debugfs_create_dir(card_name, cd->debugfs_genwqe); root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
if (!root) { if (!root) {
...@@ -454,7 +454,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd) ...@@ -454,7 +454,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
} }
for (i = 0; i < GENWQE_MAX_VFS; i++) { for (i = 0; i < GENWQE_MAX_VFS; i++) {
sprintf(name, "vf%d_jobtimeout_msec", i); sprintf(name, "vf%u_jobtimeout_msec", i);
file = debugfs_create_u32(name, 0666, root, file = debugfs_create_u32(name, 0666, root,
&cd->vf_jobtimeout_msec[i]); &cd->vf_jobtimeout_msec[i]);
......
...@@ -454,7 +454,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, ...@@ -454,7 +454,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
*/ */
int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl) int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl)
{ {
int rc; int rc = 0;
struct pci_dev *pci_dev = cd->pci_dev; struct pci_dev *pci_dev = cd->pci_dev;
if (sgl->fpage) { if (sgl->fpage) {
......
...@@ -111,8 +111,6 @@ int mei_amthif_host_init(struct mei_device *dev) ...@@ -111,8 +111,6 @@ int mei_amthif_host_init(struct mei_device *dev)
return ret; return ret;
} }
cl->state = MEI_FILE_CONNECTING;
ret = mei_cl_connect(cl, NULL); ret = mei_cl_connect(cl, NULL);
dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_state = MEI_IAMTHIF_IDLE;
......
...@@ -247,7 +247,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, ...@@ -247,7 +247,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
return id; return id;
if (length > dev->me_clients[id].props.max_msg_length) if (length > dev->me_clients[id].props.max_msg_length)
return -EINVAL; return -EFBIG;
cb = mei_io_cb_init(cl, NULL); cb = mei_io_cb_init(cl, NULL);
if (!cb) if (!cb)
...@@ -427,8 +427,6 @@ int mei_cl_enable_device(struct mei_cl_device *device) ...@@ -427,8 +427,6 @@ int mei_cl_enable_device(struct mei_cl_device *device)
mutex_lock(&dev->device_lock); mutex_lock(&dev->device_lock);
cl->state = MEI_FILE_CONNECTING;
err = mei_cl_connect(cl, NULL); err = mei_cl_connect(cl, NULL);
if (err < 0) { if (err < 0) {
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h> #include <linux/mei.h>
...@@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work) ...@@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work)
dev->reset_count = 0; dev->reset_count = 0;
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
pm_runtime_mark_last_busy(&dev->pdev->dev);
dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n");
pm_runtime_autosuspend(&dev->pdev->dev);
} }
/** /**
...@@ -425,6 +430,12 @@ void mei_host_client_init(struct work_struct *work) ...@@ -425,6 +430,12 @@ void mei_host_client_init(struct work_struct *work)
*/ */
bool mei_hbuf_acquire(struct mei_device *dev) bool mei_hbuf_acquire(struct mei_device *dev)
{ {
if (mei_pg_state(dev) == MEI_PG_ON ||
dev->pg_event == MEI_PG_EVENT_WAIT) {
dev_dbg(&dev->pdev->dev, "device is in pg\n");
return false;
}
if (!dev->hbuf_is_ready) { if (!dev->hbuf_is_ready) {
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n"); dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
return false; return false;
...@@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl) ...@@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
if (cl->state != MEI_FILE_DISCONNECTING) if (cl->state != MEI_FILE_DISCONNECTING)
return 0; return 0;
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, NULL); cb = mei_io_cb_init(cl, NULL);
if (!cb) if (!cb) {
return -ENOMEM; rets = -ENOMEM;
goto free;
}
cb->fop_type = MEI_FOP_CLOSE; cb->fop_type = MEI_FOP_CLOSE;
if (mei_hbuf_acquire(dev)) { if (mei_hbuf_acquire(dev)) {
...@@ -494,8 +514,7 @@ int mei_cl_disconnect(struct mei_cl *cl) ...@@ -494,8 +514,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
cl_err(dev, cl, "wrong status client disconnect.\n"); cl_err(dev, cl, "wrong status client disconnect.\n");
if (err) if (err)
cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
err);
cl_err(dev, cl, "failed to disconnect from FW client.\n"); cl_err(dev, cl, "failed to disconnect from FW client.\n");
} }
...@@ -503,6 +522,10 @@ int mei_cl_disconnect(struct mei_cl *cl) ...@@ -503,6 +522,10 @@ int mei_cl_disconnect(struct mei_cl *cl)
mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl);
free: free:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
mei_io_cb_free(cb); mei_io_cb_free(cb);
return rets; return rets;
} }
...@@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) ...@@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
dev = cl->dev; dev = cl->dev;
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, file); cb = mei_io_cb_init(cl, file);
if (!cb) { if (!cb) {
rets = -ENOMEM; rets = -ENOMEM;
...@@ -567,6 +597,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) ...@@ -567,6 +597,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
/* run hbuf acquire last so we don't have to undo */ /* run hbuf acquire last so we don't have to undo */
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
cl->state = MEI_FILE_CONNECTING;
if (mei_hbm_cl_connect_req(dev, cl)) { if (mei_hbm_cl_connect_req(dev, cl)) {
rets = -ENODEV; rets = -ENODEV;
goto out; goto out;
...@@ -596,6 +627,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) ...@@ -596,6 +627,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
rets = cl->status; rets = cl->status;
out: out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
mei_io_cb_free(cb); mei_io_cb_free(cb);
return rets; return rets;
} }
...@@ -713,23 +748,31 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) ...@@ -713,23 +748,31 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
return -ENOTTY; return -ENOTTY;
} }
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, NULL); cb = mei_io_cb_init(cl, NULL);
if (!cb) if (!cb) {
return -ENOMEM; rets = -ENOMEM;
goto out;
}
/* always allocate at least client max message */ /* always allocate at least client max message */
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
rets = mei_io_cb_alloc_resp_buf(cb, length); rets = mei_io_cb_alloc_resp_buf(cb, length);
if (rets) if (rets)
goto err; goto out;
cb->fop_type = MEI_FOP_READ; cb->fop_type = MEI_FOP_READ;
if (mei_hbuf_acquire(dev)) { if (mei_hbuf_acquire(dev)) {
if (mei_hbm_cl_flow_control_req(dev, cl)) { rets = mei_hbm_cl_flow_control_req(dev, cl);
cl_err(dev, cl, "flow control send failed\n"); if (rets < 0)
rets = -ENODEV; goto out;
goto err;
}
list_add_tail(&cb->list, &dev->read_list.list); list_add_tail(&cb->list, &dev->read_list.list);
} else { } else {
list_add_tail(&cb->list, &dev->ctrl_wr_list.list); list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
...@@ -737,9 +780,14 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) ...@@ -737,9 +780,14 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
cl->read_cb = cb; cl->read_cb = cb;
return rets; out:
err: cl_dbg(dev, cl, "rpm: autosuspend\n");
mei_io_cb_free(cb); pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
if (rets)
mei_io_cb_free(cb);
return rets; return rets;
} }
...@@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, ...@@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return rets; return rets;
if (rets == 0) { if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
return 0; return 0;
} }
...@@ -856,6 +904,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) ...@@ -856,6 +904,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb->fop_type = MEI_FOP_WRITE; cb->fop_type = MEI_FOP_WRITE;
cb->buf_idx = 0; cb->buf_idx = 0;
...@@ -926,6 +980,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) ...@@ -926,6 +980,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
rets = buf->size; rets = buf->size;
err: err:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
return rets; return rets;
} }
......
...@@ -14,10 +14,12 @@ ...@@ -14,10 +14,12 @@
* *
*/ */
#include <linux/export.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/mei.h> #include <linux/mei.h>
#include <linux/pm_runtime.h>
#include "mei_dev.h" #include "mei_dev.h"
#include "hbm.h" #include "hbm.h"
...@@ -57,6 +59,34 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) ...@@ -57,6 +59,34 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
} }
} }
/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
*/
void mei_hbm_idle(struct mei_device *dev)
{
dev->init_clients_timer = 0;
dev->hbm_state = MEI_HBM_IDLE;
}
/**
* mei_hbm_reset - reset hbm counters and book keeping data structurs
*
* @dev: the device structure
*/
void mei_hbm_reset(struct mei_device *dev)
{
dev->me_clients_num = 0;
dev->me_client_presentation_num = 0;
dev->me_client_index = 0;
kfree(dev->me_clients);
dev->me_clients = NULL;
mei_hbm_idle(dev);
}
/** /**
* mei_hbm_me_cl_allocate - allocates storage for me clients * mei_hbm_me_cl_allocate - allocates storage for me clients
* *
...@@ -69,9 +99,7 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev) ...@@ -69,9 +99,7 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
struct mei_me_client *clients; struct mei_me_client *clients;
int b; int b;
dev->me_clients_num = 0; mei_hbm_reset(dev);
dev->me_client_presentation_num = 0;
dev->me_client_index = 0;
/* count how many ME clients we have */ /* count how many ME clients we have */
for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
...@@ -80,9 +108,6 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev) ...@@ -80,9 +108,6 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
if (dev->me_clients_num == 0) if (dev->me_clients_num == 0)
return 0; return 0;
kfree(dev->me_clients);
dev->me_clients = NULL;
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n", dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
dev->me_clients_num * sizeof(struct mei_me_client)); dev->me_clients_num * sizeof(struct mei_me_client));
/* allocate storage for ME clients representation */ /* allocate storage for ME clients representation */
...@@ -133,17 +158,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf) ...@@ -133,17 +158,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
} }
/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
*/
void mei_hbm_idle(struct mei_device *dev)
{
dev->init_clients_timer = 0;
dev->hbm_state = MEI_HBM_IDLE;
}
int mei_hbm_start_wait(struct mei_device *dev) int mei_hbm_start_wait(struct mei_device *dev)
{ {
int ret; int ret;
...@@ -289,6 +303,34 @@ static int mei_hbm_prop_req(struct mei_device *dev) ...@@ -289,6 +303,34 @@ static int mei_hbm_prop_req(struct mei_device *dev)
return 0; return 0;
} }
/*
* mei_hbm_pg - sends pg command
*
* @dev: the device structure
* @pg_cmd: the pg command code
*
* This function returns -EIO on write failure
*/
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
{
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
struct hbm_power_gate *req;
const size_t len = sizeof(struct hbm_power_gate);
int ret;
mei_hbm_hdr(mei_hdr, len);
req = (struct hbm_power_gate *)dev->wr_msg.data;
memset(req, 0, len);
req->hbm_cmd = pg_cmd;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret)
dev_err(&dev->pdev->dev, "power gate command write failed.\n");
return ret;
}
EXPORT_SYMBOL_GPL(mei_hbm_pg);
/** /**
* mei_hbm_stop_req - send stop request message * mei_hbm_stop_req - send stop request message
* *
...@@ -701,6 +743,27 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) ...@@ -701,6 +743,27 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
mei_hbm_cl_flow_control_res(dev, flow_control); mei_hbm_cl_flow_control_res(dev, flow_control);
break; break;
case MEI_PG_ISOLATION_ENTRY_RES_CMD:
dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&dev->wait_pg))
wake_up(&dev->wait_pg);
break;
case MEI_PG_ISOLATION_EXIT_REQ_CMD:
dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&dev->wait_pg))
wake_up(&dev->wait_pg);
else
/*
* If the driver is not waiting on this then
* this is HW initiated exit from PG.
* Start runtime pm resume sequence to exit from PG.
*/
pm_request_resume(&dev->pdev->dev);
break;
case HOST_CLIENT_PROPERTIES_RES_CMD: case HOST_CLIENT_PROPERTIES_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n"); dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
......
...@@ -50,6 +50,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length) ...@@ -50,6 +50,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
} }
void mei_hbm_idle(struct mei_device *dev); void mei_hbm_idle(struct mei_device *dev);
void mei_hbm_reset(struct mei_device *dev);
int mei_hbm_start_req(struct mei_device *dev); int mei_hbm_start_req(struct mei_device *dev);
int mei_hbm_start_wait(struct mei_device *dev); int mei_hbm_start_wait(struct mei_device *dev);
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
...@@ -57,6 +58,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl); ...@@ -57,6 +58,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
bool mei_hbm_version_is_supported(struct mei_device *dev); bool mei_hbm_version_is_supported(struct mei_device *dev);
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd);
#endif /* _MEI_HBM_H_ */ #endif /* _MEI_HBM_H_ */
...@@ -133,6 +133,8 @@ ...@@ -133,6 +133,8 @@
#define ME_CB_RW 8 #define ME_CB_RW 8
/* ME_CSR_HA - ME Control Status Host Access register (read only) */ /* ME_CSR_HA - ME Control Status Host Access register (read only) */
#define ME_CSR_HA 0xC #define ME_CSR_HA 0xC
/* H_HGC_CSR - PGI register */
#define H_HPG_CSR 0x10
/* register bits of H_CSR (Host Control Status register) */ /* register bits of H_CSR (Host Control Status register) */
...@@ -162,6 +164,8 @@ access to ME_CBD */ ...@@ -162,6 +164,8 @@ access to ME_CBD */
#define ME_CBWP_HRA 0x00FF0000 #define ME_CBWP_HRA 0x00FF0000
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */ /* ME CB Read Pointer HRA - host read only access to ME_CBRP */
#define ME_CBRP_HRA 0x0000FF00 #define ME_CBRP_HRA 0x0000FF00
/* ME Power Gate Isolation Capability HRA - host ready only access */
#define ME_PGIC_HRA 0x00000040
/* ME Reset HRA - host read only access to ME_RST */ /* ME Reset HRA - host read only access to ME_RST */
#define ME_RST_HRA 0x00000010 #define ME_RST_HRA 0x00000010
/* ME Ready HRA - host read only access to ME_RDY */ /* ME Ready HRA - host read only access to ME_RDY */
...@@ -173,4 +177,9 @@ access to ME_CBD */ ...@@ -173,4 +177,9 @@ access to ME_CBD */
/* ME Interrupt Enable HRA - host read only access to ME_IE */ /* ME Interrupt Enable HRA - host read only access to ME_IE */
#define ME_IE_HRA 0x00000001 #define ME_IE_HRA 0x00000001
/* register bits - H_HPG_CSR */
#define H_HPG_CSR_PGIHEXR 0x00000001
#define H_HPG_CSR_PGI 0x00000002
#endif /* _MEI_HW_MEI_REGS_H_ */ #endif /* _MEI_HW_MEI_REGS_H_ */
This diff is collapsed.
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include "mei_dev.h" #include "mei_dev.h"
#include "client.h" #include "client.h"
#define MEI_ME_RPM_TIMEOUT 500 /* ms */
struct mei_me_hw { struct mei_me_hw {
void __iomem *mem_addr; void __iomem *mem_addr;
/* /*
...@@ -31,11 +33,22 @@ struct mei_me_hw { ...@@ -31,11 +33,22 @@ struct mei_me_hw {
*/ */
u32 host_hw_state; u32 host_hw_state;
u32 me_hw_state; u32 me_hw_state;
enum mei_pg_state pg_state;
}; };
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw) #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
struct mei_device *mei_me_dev_init(struct pci_dev *pdev); extern const struct mei_cfg mei_me_legacy_cfg;
extern const struct mei_cfg mei_me_ich_cfg;
extern const struct mei_cfg mei_me_pch_cfg;
extern const struct mei_cfg mei_me_pch_cpt_pbg_cfg;
extern const struct mei_cfg mei_me_lpt_cfg;
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg);
int mei_me_pg_set_sync(struct mei_device *dev);
int mei_me_pg_unset_sync(struct mei_device *dev);
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id); irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
......
...@@ -89,7 +89,7 @@ enum { ...@@ -89,7 +89,7 @@ enum {
# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000 # define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000
# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000 # define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000
# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000 # define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000
#define PCI_CFG_TXE_FW_STS1 0x48
#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */ #define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */
......
...@@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) ...@@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req)
dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
hw->aliveness, req); hw->aliveness, req);
if (do_req) { if (do_req) {
hw->recvd_aliveness = false; dev->pg_event = MEI_PG_EVENT_WAIT;
mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req); mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
} }
return do_req; return do_req;
...@@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) ...@@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
do { do {
hw->aliveness = mei_txe_aliveness_get(dev); hw->aliveness = mei_txe_aliveness_get(dev);
if (hw->aliveness == expected) { if (hw->aliveness == expected) {
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_dbg(&dev->pdev->dev, dev_dbg(&dev->pdev->dev,
"aliveness settled after %d msecs\n", t); "aliveness settled after %d msecs\n", t);
return t; return t;
...@@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) ...@@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
t += MSEC_PER_SEC / 5; t += MSEC_PER_SEC / 5;
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT); } while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_err(&dev->pdev->dev, "aliveness timed out\n"); dev_err(&dev->pdev->dev, "aliveness timed out\n");
return -ETIME; return -ETIME;
} }
...@@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) ...@@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
return 0; return 0;
mutex_unlock(&dev->device_lock); mutex_unlock(&dev->device_lock);
err = wait_event_timeout(hw->wait_aliveness, err = wait_event_timeout(hw->wait_aliveness_resp,
hw->recvd_aliveness, timeout); dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
mutex_lock(&dev->device_lock); mutex_lock(&dev->device_lock);
hw->aliveness = mei_txe_aliveness_get(dev); hw->aliveness = mei_txe_aliveness_get(dev);
ret = hw->aliveness == expected ? 0 : -ETIME; ret = hw->aliveness == expected ? 0 : -ETIME;
if (ret) if (ret)
dev_err(&dev->pdev->dev, "aliveness timed out"); dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n",
err, hw->aliveness, dev->pg_event);
else else
dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n", dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n",
jiffies_to_msecs(timeout - err)); jiffies_to_msecs(timeout - err),
hw->recvd_aliveness = false; hw->aliveness, dev->pg_event);
dev->pg_event = MEI_PG_EVENT_IDLE;
return ret; return ret;
} }
...@@ -279,6 +284,32 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) ...@@ -279,6 +284,32 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
return 0; return 0;
} }
/**
* mei_txe_pg_is_enabled - detect if PG is supported by HW
*
* @dev: the device structure
*
* returns: true is pg supported, false otherwise
*/
static bool mei_txe_pg_is_enabled(struct mei_device *dev)
{
return true;
}
/**
* mei_txe_pg_state - translate aliveness register value
* to the mei power gating state
*
* @dev: the device structure
*
* returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
*/
static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev)
{
struct mei_txe_hw *hw = to_txe_hw(dev);
return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON;
}
/** /**
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt * mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
* *
...@@ -589,7 +620,10 @@ static int mei_txe_write(struct mei_device *dev, ...@@ -589,7 +620,10 @@ static int mei_txe_write(struct mei_device *dev,
mei_txe_input_ready_interrupt_enable(dev); mei_txe_input_ready_interrupt_enable(dev);
if (!mei_txe_is_input_ready(dev)) { if (!mei_txe_is_input_ready(dev)) {
dev_err(&dev->pdev->dev, "Input is not ready"); struct mei_fw_status fw_status;
mei_fw_status(dev, &fw_status);
dev_err(&dev->pdev->dev, "Input is not ready " FW_STS_FMT "\n",
FW_STS_PRM(fw_status));
return -EAGAIN; return -EAGAIN;
} }
...@@ -960,9 +994,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) ...@@ -960,9 +994,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
/* Clear the interrupt cause */ /* Clear the interrupt cause */
dev_dbg(&dev->pdev->dev, dev_dbg(&dev->pdev->dev,
"Aliveness Interrupt: Status: %d\n", hw->aliveness); "Aliveness Interrupt: Status: %d\n", hw->aliveness);
hw->recvd_aliveness = true; dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&hw->wait_aliveness)) if (waitqueue_active(&hw->wait_aliveness_resp))
wake_up(&hw->wait_aliveness); wake_up(&hw->wait_aliveness_resp);
} }
...@@ -1008,15 +1042,51 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) ...@@ -1008,15 +1042,51 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/**
* mei_txe_fw_status - retrieve fw status from the pci config space
*
* @dev: the device structure
* @fw_status: fw status registers storage
*
* returns: 0 on success an error code otherwise
*/
static int mei_txe_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
const u32 pci_cfg_reg[] = {PCI_CFG_TXE_FW_STS0, PCI_CFG_TXE_FW_STS1};
int i;
if (!fw_status)
return -EINVAL;
fw_status->count = 2;
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
pci_cfg_reg[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
static const struct mei_hw_ops mei_txe_hw_ops = { static const struct mei_hw_ops mei_txe_hw_ops = {
.fw_status = mei_txe_fw_status,
.host_is_ready = mei_txe_host_is_ready, .host_is_ready = mei_txe_host_is_ready,
.pg_state = mei_txe_pg_state,
.hw_is_ready = mei_txe_hw_is_ready, .hw_is_ready = mei_txe_hw_is_ready,
.hw_reset = mei_txe_hw_reset, .hw_reset = mei_txe_hw_reset,
.hw_config = mei_txe_hw_config, .hw_config = mei_txe_hw_config,
.hw_start = mei_txe_hw_start, .hw_start = mei_txe_hw_start,
.pg_is_enabled = mei_txe_pg_is_enabled,
.intr_clear = mei_txe_intr_clear, .intr_clear = mei_txe_intr_clear,
.intr_enable = mei_txe_intr_enable, .intr_enable = mei_txe_intr_enable,
.intr_disable = mei_txe_intr_disable, .intr_disable = mei_txe_intr_disable,
...@@ -1034,14 +1104,27 @@ static const struct mei_hw_ops mei_txe_hw_ops = { ...@@ -1034,14 +1104,27 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
}; };
#define MEI_CFG_TXE_FW_STS \
.fw_status.count = 2, \
.fw_status.status[0] = PCI_CFG_TXE_FW_STS0, \
.fw_status.status[1] = PCI_CFG_TXE_FW_STS1
const struct mei_cfg mei_txe_cfg = {
MEI_CFG_TXE_FW_STS,
};
/** /**
* mei_txe_dev_init - allocates and initializes txe hardware specific structure * mei_txe_dev_init - allocates and initializes txe hardware specific structure
* *
* @pdev - pci device * @pdev - pci device
* @cfg - per device generation config
*
* returns struct mei_device * on success or NULL; * returns struct mei_device * on success or NULL;
* *
*/ */
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg)
{ {
struct mei_device *dev; struct mei_device *dev;
struct mei_txe_hw *hw; struct mei_txe_hw *hw;
...@@ -1051,11 +1134,11 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) ...@@ -1051,11 +1134,11 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
if (!dev) if (!dev)
return NULL; return NULL;
mei_device_init(dev); mei_device_init(dev, cfg);
hw = to_txe_hw(dev); hw = to_txe_hw(dev);
init_waitqueue_head(&hw->wait_aliveness); init_waitqueue_head(&hw->wait_aliveness_resp);
dev->ops = &mei_txe_hw_ops; dev->ops = &mei_txe_hw_ops;
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "hw.h" #include "hw.h"
#include "hw-txe-regs.h" #include "hw-txe-regs.h"
#define MEI_TXI_RPM_TIMEOUT 500 /* ms */
/* Flatten Hierarchy interrupt cause */ /* Flatten Hierarchy interrupt cause */
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */ #define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
#define TXE_INTR_READINESS HISR_INT_0_STS #define TXE_INTR_READINESS HISR_INT_0_STS
...@@ -35,12 +37,11 @@ ...@@ -35,12 +37,11 @@
/** /**
* struct mei_txe_hw - txe hardware specifics * struct mei_txe_hw - txe hardware specifics
* *
* @mem_addr: SeC and BRIDGE bars * @mem_addr: SeC and BRIDGE bars
* @aliveness: aliveness (power gating) state of the hardware * @aliveness: aliveness (power gating) state of the hardware
* @readiness: readiness state of the hardware * @readiness: readiness state of the hardware
* @wait_aliveness: aliveness wait queue * @wait_aliveness_resp: aliveness wait queue
* @recvd_aliveness: aliveness interrupt was recived * @intr_cause: translated interrupt cause
* @intr_cause: translated interrupt cause
*/ */
struct mei_txe_hw { struct mei_txe_hw {
void __iomem *mem_addr[NUM_OF_MEM_BARS]; void __iomem *mem_addr[NUM_OF_MEM_BARS];
...@@ -48,8 +49,7 @@ struct mei_txe_hw { ...@@ -48,8 +49,7 @@ struct mei_txe_hw {
u32 readiness; u32 readiness;
u32 slots; u32 slots;
wait_queue_head_t wait_aliveness; wait_queue_head_t wait_aliveness_resp;
bool recvd_aliveness;
unsigned long intr_cause; unsigned long intr_cause;
}; };
...@@ -61,7 +61,10 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw) ...@@ -61,7 +61,10 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw)
return container_of((void *)hw, struct mei_device, hw); return container_of((void *)hw, struct mei_device, hw);
} }
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev); extern const struct mei_cfg mei_txe_cfg;
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg);
irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id); irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id); irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
......
...@@ -31,14 +31,21 @@ ...@@ -31,14 +31,21 @@
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ #define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */
#define MEI_HBM_TIMEOUT 1 /* 1 second */ #define MEI_HBM_TIMEOUT 1 /* 1 second */
/* /*
* MEI Version * MEI Version
*/ */
#define HBM_MINOR_VERSION 0 #define HBM_MINOR_VERSION 1
#define HBM_MAJOR_VERSION 1 #define HBM_MAJOR_VERSION 1
/*
* MEI version with PGI support
*/
#define HBM_MINOR_VERSION_PGI 1
#define HBM_MAJOR_VERSION_PGI 1
/* Host bus message command opcode */ /* Host bus message command opcode */
#define MEI_HBM_CMD_OP_MSK 0x7f #define MEI_HBM_CMD_OP_MSK 0x7f
/* Host bus message command RESPONSE */ /* Host bus message command RESPONSE */
...@@ -69,6 +76,11 @@ ...@@ -69,6 +76,11 @@
#define MEI_FLOW_CONTROL_CMD 0x08 #define MEI_FLOW_CONTROL_CMD 0x08
#define MEI_PG_ISOLATION_ENTRY_REQ_CMD 0x0a
#define MEI_PG_ISOLATION_ENTRY_RES_CMD 0x8a
#define MEI_PG_ISOLATION_EXIT_REQ_CMD 0x0b
#define MEI_PG_ISOLATION_EXIT_RES_CMD 0x8b
/* /*
* MEI Stop Reason * MEI Stop Reason
* used by hbm_host_stop_request.reason * used by hbm_host_stop_request.reason
...@@ -207,6 +219,17 @@ struct hbm_props_response { ...@@ -207,6 +219,17 @@ struct hbm_props_response {
struct mei_client_properties client_properties; struct mei_client_properties client_properties;
} __packed; } __packed;
/**
* struct hbm_power_gate - power gate request/response
*
* @hbm_cmd - bus message command header
* @reserved[3]
*/
struct hbm_power_gate {
u8 hbm_cmd;
u8 reserved[3];
} __packed;
/** /**
* struct hbm_client_connect_request - connect/disconnect request * struct hbm_client_connect_request - connect/disconnect request
* *
......
...@@ -74,9 +74,13 @@ int mei_reset(struct mei_device *dev) ...@@ -74,9 +74,13 @@ int mei_reset(struct mei_device *dev)
if (state != MEI_DEV_INITIALIZING && if (state != MEI_DEV_INITIALIZING &&
state != MEI_DEV_DISABLED && state != MEI_DEV_DISABLED &&
state != MEI_DEV_POWER_DOWN && state != MEI_DEV_POWER_DOWN &&
state != MEI_DEV_POWER_UP) state != MEI_DEV_POWER_UP) {
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", struct mei_fw_status fw_status;
mei_dev_state_str(state)); mei_fw_status(dev, &fw_status);
dev_warn(&dev->pdev->dev,
"unexpected reset: dev_state = %s " FW_STS_FMT "\n",
mei_dev_state_str(state), FW_STS_PRM(fw_status));
}
/* we're already in reset, cancel the init timer /* we're already in reset, cancel the init timer
* if the reset was called due the hbm protocol error * if the reset was called due the hbm protocol error
...@@ -118,8 +122,8 @@ int mei_reset(struct mei_device *dev) ...@@ -118,8 +122,8 @@ int mei_reset(struct mei_device *dev)
mei_amthif_reset_params(dev); mei_amthif_reset_params(dev);
} }
mei_hbm_reset(dev);
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0; dev->rd_msg_hdr = 0;
dev->wd_pending = false; dev->wd_pending = false;
...@@ -303,15 +307,58 @@ void mei_stop(struct mei_device *dev) ...@@ -303,15 +307,58 @@ void mei_stop(struct mei_device *dev)
} }
EXPORT_SYMBOL_GPL(mei_stop); EXPORT_SYMBOL_GPL(mei_stop);
/**
* mei_write_is_idle - check if the write queues are idle
*
* @dev: the device structure
*
* returns true of there is no pending write
*/
bool mei_write_is_idle(struct mei_device *dev)
{
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
list_empty(&dev->ctrl_wr_list.list) &&
list_empty(&dev->write_list.list));
dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
idle,
mei_dev_state_str(dev->dev_state),
list_empty(&dev->ctrl_wr_list.list),
list_empty(&dev->write_list.list));
void mei_device_init(struct mei_device *dev) return idle;
}
EXPORT_SYMBOL_GPL(mei_write_is_idle);
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status)
{
int i;
const struct mei_fw_status *fw_src = &dev->cfg->fw_status;
if (!fw_status)
return -EINVAL;
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
fw_src->status[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(mei_fw_status);
void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg)
{ {
/* setup our list array */ /* setup our list array */
INIT_LIST_HEAD(&dev->file_list); INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list); INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock); mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_pg);
init_waitqueue_head(&dev->wait_recvd_msg); init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd); init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING; dev->dev_state = MEI_DEV_INITIALIZING;
...@@ -340,6 +387,9 @@ void mei_device_init(struct mei_device *dev) ...@@ -340,6 +387,9 @@ void mei_device_init(struct mei_device *dev)
* 0: Reserved for MEI Bus Message communications * 0: Reserved for MEI Bus Message communications
*/ */
bitmap_set(dev->host_clients_map, 0, 1); bitmap_set(dev->host_clients_map, 0, 1);
dev->pg_event = MEI_PG_EVENT_IDLE;
dev->cfg = cfg;
} }
EXPORT_SYMBOL_GPL(mei_device_init); EXPORT_SYMBOL_GPL(mei_device_init);
...@@ -467,7 +467,6 @@ static int mei_ioctl_connect_client(struct file *file, ...@@ -467,7 +467,6 @@ static int mei_ioctl_connect_client(struct file *file,
} }
cl->me_client_id = dev->me_clients[i].client_id; cl->me_client_id = dev->me_clients[i].client_id;
cl->state = MEI_FILE_CONNECTING;
dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
cl->me_client_id); cl->me_client_id);
......
...@@ -153,6 +153,20 @@ struct mei_msg_data { ...@@ -153,6 +153,20 @@ struct mei_msg_data {
unsigned char *data; unsigned char *data;
}; };
/* Maximum number of processed FW status registers */
#define MEI_FW_STATUS_MAX 2
/*
* struct mei_fw_status - storage of FW status data
*
* @count - number of actually available elements in array
* @status - FW status registers
*/
struct mei_fw_status {
int count;
u32 status[MEI_FW_STATUS_MAX];
};
/** /**
* struct mei_me_client - representation of me (fw) client * struct mei_me_client - representation of me (fw) client
* *
...@@ -213,6 +227,7 @@ struct mei_cl { ...@@ -213,6 +227,7 @@ struct mei_cl {
/** struct mei_hw_ops /** struct mei_hw_ops
* *
* @fw_status - read FW status from PCI config space
* @host_is_ready - query for host readiness * @host_is_ready - query for host readiness
* @hw_is_ready - query if hw is ready * @hw_is_ready - query if hw is ready
...@@ -220,6 +235,9 @@ struct mei_cl { ...@@ -220,6 +235,9 @@ struct mei_cl {
* @hw_start - start hw after reset * @hw_start - start hw after reset
* @hw_config - configure hw * @hw_config - configure hw
* @pg_state - power gating state of the device
* @pg_is_enabled - is power gating enabled
* @intr_clear - clear pending interrupts * @intr_clear - clear pending interrupts
* @intr_enable - enable interrupts * @intr_enable - enable interrupts
* @intr_disable - disable interrupts * @intr_disable - disable interrupts
...@@ -237,6 +255,8 @@ struct mei_cl { ...@@ -237,6 +255,8 @@ struct mei_cl {
*/ */
struct mei_hw_ops { struct mei_hw_ops {
int (*fw_status)(struct mei_device *dev,
struct mei_fw_status *fw_status);
bool (*host_is_ready)(struct mei_device *dev); bool (*host_is_ready)(struct mei_device *dev);
bool (*hw_is_ready)(struct mei_device *dev); bool (*hw_is_ready)(struct mei_device *dev);
...@@ -244,6 +264,9 @@ struct mei_hw_ops { ...@@ -244,6 +264,9 @@ struct mei_hw_ops {
int (*hw_start)(struct mei_device *dev); int (*hw_start)(struct mei_device *dev);
void (*hw_config)(struct mei_device *dev); void (*hw_config)(struct mei_device *dev);
enum mei_pg_state (*pg_state)(struct mei_device *dev);
bool (*pg_is_enabled)(struct mei_device *dev);
void (*intr_clear)(struct mei_device *dev); void (*intr_clear)(struct mei_device *dev);
void (*intr_enable)(struct mei_device *dev); void (*intr_enable)(struct mei_device *dev);
void (*intr_disable)(struct mei_device *dev); void (*intr_disable)(struct mei_device *dev);
...@@ -331,16 +354,61 @@ struct mei_cl_device { ...@@ -331,16 +354,61 @@ struct mei_cl_device {
void *priv_data; void *priv_data;
}; };
/**
* enum mei_pg_event - power gating transition events
*
* @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
* @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
* @MEI_PG_EVENT_RECEIVED: the driver received pg event
*/
enum mei_pg_event {
MEI_PG_EVENT_IDLE,
MEI_PG_EVENT_WAIT,
MEI_PG_EVENT_RECEIVED,
};
/**
* enum mei_pg_state - device internal power gating state
*
* @MEI_PG_OFF: device is not power gated - it is active
* @MEI_PG_ON: device is power gated - it is in lower power state
*/
enum mei_pg_state {
MEI_PG_OFF = 0,
MEI_PG_ON = 1,
};
/*
* mei_cfg
*
* @fw_status - FW status
* @quirk_probe - device exclusion quirk
*/
struct mei_cfg {
const struct mei_fw_status fw_status;
bool (*quirk_probe)(struct pci_dev *pdev);
};
#define MEI_PCI_DEVICE(dev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
.driver_data = (kernel_ulong_t)&(cfg)
/** /**
* struct mei_device - MEI private device struct * struct mei_device - MEI private device struct
* @reset_count - limits the number of consecutive resets * @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol * @hbm_state - state of host bus message protocol
* @pg_event - power gating event
* @mem_addr - mem mapped base register address * @mem_addr - mem mapped base register address
* @hbuf_depth - depth of hardware host/write buffer is slots * @hbuf_depth - depth of hardware host/write buffer is slots
* @hbuf_is_ready - query if the host host/write buffer is ready * @hbuf_is_ready - query if the host host/write buffer is ready
* @wr_msg - the buffer for hbm control messages * @wr_msg - the buffer for hbm control messages
* @cfg - per device generation config and ops
*/ */
struct mei_device { struct mei_device {
struct pci_dev *pdev; /* pointer to pci device struct */ struct pci_dev *pdev; /* pointer to pci device struct */
...@@ -371,6 +439,7 @@ struct mei_device { ...@@ -371,6 +439,7 @@ struct mei_device {
* waiting queue for receive message from FW * waiting queue for receive message from FW
*/ */
wait_queue_head_t wait_hw_ready; wait_queue_head_t wait_hw_ready;
wait_queue_head_t wait_pg;
wait_queue_head_t wait_recvd_msg; wait_queue_head_t wait_recvd_msg;
wait_queue_head_t wait_stop_wd; wait_queue_head_t wait_stop_wd;
...@@ -382,6 +451,14 @@ struct mei_device { ...@@ -382,6 +451,14 @@ struct mei_device {
enum mei_hbm_state hbm_state; enum mei_hbm_state hbm_state;
u16 init_clients_timer; u16 init_clients_timer;
/*
* Power Gating support
*/
enum mei_pg_event pg_event;
#ifdef CONFIG_PM_RUNTIME
struct dev_pm_domain pg_domain;
#endif /* CONFIG_PM_RUNTIME */
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */ unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
u32 rd_msg_hdr; u32 rd_msg_hdr;
...@@ -442,6 +519,7 @@ struct mei_device { ...@@ -442,6 +519,7 @@ struct mei_device {
const struct mei_hw_ops *ops; const struct mei_hw_ops *ops;
const struct mei_cfg *cfg;
char hw[0] __aligned(sizeof(void *)); char hw[0] __aligned(sizeof(void *));
}; };
...@@ -474,7 +552,7 @@ static inline u32 mei_slots2data(int slots) ...@@ -474,7 +552,7 @@ static inline u32 mei_slots2data(int slots)
/* /*
* mei init function prototypes * mei init function prototypes
*/ */
void mei_device_init(struct mei_device *dev); void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg);
int mei_reset(struct mei_device *dev); int mei_reset(struct mei_device *dev);
int mei_start(struct mei_device *dev); int mei_start(struct mei_device *dev);
int mei_restart(struct mei_device *dev); int mei_restart(struct mei_device *dev);
...@@ -553,10 +631,22 @@ void mei_watchdog_unregister(struct mei_device *dev); ...@@ -553,10 +631,22 @@ void mei_watchdog_unregister(struct mei_device *dev);
* Register Access Function * Register Access Function
*/ */
static inline void mei_hw_config(struct mei_device *dev) static inline void mei_hw_config(struct mei_device *dev)
{ {
dev->ops->hw_config(dev); dev->ops->hw_config(dev);
} }
static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
{
return dev->ops->pg_state(dev);
}
static inline bool mei_pg_is_enabled(struct mei_device *dev)
{
return dev->ops->pg_is_enabled(dev);
}
static inline int mei_hw_reset(struct mei_device *dev, bool enable) static inline int mei_hw_reset(struct mei_device *dev, bool enable)
{ {
return dev->ops->hw_reset(dev, enable); return dev->ops->hw_reset(dev, enable);
...@@ -629,8 +719,17 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) ...@@ -629,8 +719,17 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
return dev->ops->rdbuf_full_slots(dev); return dev->ops->rdbuf_full_slots(dev);
} }
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status);
#define FW_STS_FMT "%08X %08X"
#define FW_STS_PRM(fw_status) \
(fw_status).count > 0 ? (fw_status).status[0] : 0xDEADBEEF, \
(fw_status).count > 1 ? (fw_status).status[1] : 0xDEADBEEF
bool mei_hbuf_acquire(struct mei_device *dev); bool mei_hbuf_acquire(struct mei_device *dev);
bool mei_write_is_idle(struct mei_device *dev);
#if IS_ENABLED(CONFIG_DEBUG_FS) #if IS_ENABLED(CONFIG_DEBUG_FS)
int mei_dbgfs_register(struct mei_device *dev, const char *name); int mei_dbgfs_register(struct mei_device *dev, const char *name);
void mei_dbgfs_deregister(struct mei_device *dev); void mei_dbgfs_deregister(struct mei_device *dev);
......
This diff is collapsed.
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h> #include <linux/mei.h>
...@@ -35,11 +36,18 @@ ...@@ -35,11 +36,18 @@
#include "hw-txe.h" #include "hw-txe.h"
static const struct pci_device_id mei_txe_pci_tbl[] = { static const struct pci_device_id mei_txe_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */ {MEI_PCI_DEVICE(0x0F18, mei_txe_cfg)}, /* Baytrail */
{0, } {0, }
}; };
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
#ifdef CONFIG_PM_RUNTIME
static inline void mei_txe_set_pm_domain(struct mei_device *dev);
static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
#else
static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
#endif /* CONFIG_PM_RUNTIME */
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
{ {
...@@ -61,6 +69,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) ...@@ -61,6 +69,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
*/ */
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{ {
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
struct mei_device *dev; struct mei_device *dev;
struct mei_txe_hw *hw; struct mei_txe_hw *hw;
int err; int err;
...@@ -91,7 +100,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -91,7 +100,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
} }
/* allocates and initializes the mei dev structure */ /* allocates and initializes the mei dev structure */
dev = mei_txe_dev_init(pdev); dev = mei_txe_dev_init(pdev, cfg);
if (!dev) { if (!dev) {
err = -ENOMEM; err = -ENOMEM;
goto release_regions; goto release_regions;
...@@ -137,12 +146,25 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -137,12 +146,25 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_irq; goto release_irq;
} }
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
err = mei_register(dev); err = mei_register(dev);
if (err) if (err)
goto release_irq; goto release_irq;
pci_set_drvdata(pdev, dev); pci_set_drvdata(pdev, dev);
/*
* For not wake-able HW runtime pm framework
* can't be used on pci device level.
* Use domain runtime pm callbacks instead.
*/
if (!pci_dev_run_wake(pdev))
mei_txe_set_pm_domain(dev);
pm_runtime_put_noidle(&pdev->dev);
return 0; return 0;
release_irq: release_irq:
...@@ -187,10 +209,15 @@ static void mei_txe_remove(struct pci_dev *pdev) ...@@ -187,10 +209,15 @@ static void mei_txe_remove(struct pci_dev *pdev)
return; return;
} }
pm_runtime_get_noresume(&pdev->dev);
hw = to_txe_hw(dev); hw = to_txe_hw(dev);
mei_stop(dev); mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_txe_unset_pm_domain(dev);
/* disable interrupts */ /* disable interrupts */
mei_disable_interrupts(dev); mei_disable_interrupts(dev);
free_irq(pdev->irq, dev); free_irq(pdev->irq, dev);
...@@ -265,15 +292,131 @@ static int mei_txe_pci_resume(struct device *device) ...@@ -265,15 +292,131 @@ static int mei_txe_pci_resume(struct device *device)
return err; return err;
} }
#endif /* CONFIG_PM_SLEEP */
#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;
}
}
static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops, /**
mei_txe_pci_suspend, * mei_txe_unset_pm_domain - clean pm domian stucture for device
mei_txe_pci_resume); *
* @dev: mei_device
*/
static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
{
/* stop using pm callbacks if any */
dev->pdev->dev.pm_domain = NULL;
}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM
static const struct dev_pm_ops mei_txe_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
mei_txe_pci_resume)
SET_RUNTIME_PM_OPS(
mei_txe_pm_runtime_suspend,
mei_txe_pm_runtime_resume,
mei_txe_pm_runtime_idle)
};
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops) #define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
#else #else
#define MEI_TXE_PM_OPS NULL #define MEI_TXE_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM */
/* /*
* PCI driver structure * PCI driver structure
*/ */
......
...@@ -84,8 +84,6 @@ int mei_wd_host_init(struct mei_device *dev) ...@@ -84,8 +84,6 @@ int mei_wd_host_init(struct mei_device *dev)
return ret; return ret;
} }
cl->state = MEI_FILE_CONNECTING;
ret = mei_cl_connect(cl, NULL); ret = mei_cl_connect(cl, NULL);
if (ret) { if (ret) {
......
...@@ -266,11 +266,12 @@ config REGULATOR_LP8788 ...@@ -266,11 +266,12 @@ config REGULATOR_LP8788
This driver supports LP8788 voltage regulator chip. This driver supports LP8788 voltage regulator chip.
config REGULATOR_MAX14577 config REGULATOR_MAX14577
tristate "Maxim 14577 regulator" tristate "Maxim 14577/77836 regulator"
depends on MFD_MAX14577 depends on MFD_MAX14577
help help
This driver controls a Maxim 14577 regulator via I2C bus. This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
The regulators include safeout LDO and current regulator 'CHARGER'. The MAX14577 regulators include safeout LDO and charger current
regulator. The MAX77836 has two additional LDOs.
config REGULATOR_MAX1586 config REGULATOR_MAX1586
tristate "Maxim 1586/1587 voltage regulator" tristate "Maxim 1586/1587 voltage regulator"
......
This diff is collapsed.
...@@ -655,7 +655,7 @@ static int uio_mmap_physical(struct vm_area_struct *vma) ...@@ -655,7 +655,7 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
if (mem->addr & ~PAGE_MASK) if (mem->addr & ~PAGE_MASK)
return -ENODEV; return -ENODEV;
if (vma->vm_end - vma->vm_start > mem->size) if (vma->vm_end - vma->vm_start > PAGE_ALIGN(mem->size))
return -EINVAL; return -EINVAL;
vma->vm_ops = &uio_physical_vm_ops; vma->vm_ops = &uio_physical_vm_ops;
......
...@@ -204,7 +204,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) ...@@ -204,7 +204,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n"); dev_err(&pdev->dev, "failed to get IRQ\n");
goto bad0; goto bad1;
} }
uioinfo->irq = ret; uioinfo->irq = ret;
} }
...@@ -275,6 +275,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) ...@@ -275,6 +275,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
ret = uio_register_device(&pdev->dev, priv->uioinfo); ret = uio_register_device(&pdev->dev, priv->uioinfo);
if (ret) { if (ret) {
dev_err(&pdev->dev, "unable to register uio device\n"); dev_err(&pdev->dev, "unable to register uio device\n");
pm_runtime_disable(&pdev->dev);
goto bad1; goto bad1;
} }
...@@ -282,7 +283,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) ...@@ -282,7 +283,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
return 0; return 0;
bad1: bad1:
kfree(priv); kfree(priv);
pm_runtime_disable(&pdev->dev);
bad0: bad0:
/* kfree uioinfo for OF */ /* kfree uioinfo for OF */
if (pdev->dev.of_node) if (pdev->dev.of_node)
......
...@@ -1078,6 +1078,8 @@ static void w1_search_process(struct w1_master *dev, u8 search_type) ...@@ -1078,6 +1078,8 @@ static void w1_search_process(struct w1_master *dev, u8 search_type)
* w1_process_callbacks() - execute each dev->async_list callback entry * w1_process_callbacks() - execute each dev->async_list callback entry
* @dev: w1_master device * @dev: w1_master device
* *
* The w1 master list_mutex must be held.
*
* Return: 1 if there were commands to executed 0 otherwise * Return: 1 if there were commands to executed 0 otherwise
*/ */
int w1_process_callbacks(struct w1_master *dev) int w1_process_callbacks(struct w1_master *dev)
......
...@@ -203,7 +203,6 @@ enum w1_master_flags { ...@@ -203,7 +203,6 @@ enum w1_master_flags {
* @search_id: allows continuing a search * @search_id: allows continuing a search
* @refcnt: reference count * @refcnt: reference count
* @priv: private data storage * @priv: private data storage
* @priv_size: size allocated
* @enable_pullup: allows a strong pullup * @enable_pullup: allows a strong pullup
* @pullup_duration: time for the next strong pullup * @pullup_duration: time for the next strong pullup
* @flags: one of w1_master_flags * @flags: one of w1_master_flags
...@@ -214,7 +213,6 @@ enum w1_master_flags { ...@@ -214,7 +213,6 @@ enum w1_master_flags {
* @dev: sysfs device * @dev: sysfs device
* @bus_master: io operations available * @bus_master: io operations available
* @seq: sequence number used for netlink broadcasts * @seq: sequence number used for netlink broadcasts
* @portid: destination for the current netlink command
*/ */
struct w1_master struct w1_master
{ {
...@@ -241,7 +239,6 @@ struct w1_master ...@@ -241,7 +239,6 @@ struct w1_master
atomic_t refcnt; atomic_t refcnt;
void *priv; void *priv;
int priv_size;
/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */ /** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
int enable_pullup; int enable_pullup;
...@@ -260,11 +257,6 @@ struct w1_master ...@@ -260,11 +257,6 @@ struct w1_master
struct w1_bus_master *bus_master; struct w1_bus_master *bus_master;
u32 seq; u32 seq;
/* port id to send netlink responses to. The value is temporarily
* stored here while processing a message, set after locking the
* mutex, zero before unlocking the mutex.
*/
u32 portid;
}; };
/** /**
......
...@@ -219,9 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev) ...@@ -219,9 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev)
if (msleep_interruptible(1000)) if (msleep_interruptible(1000))
flush_signals(current); flush_signals(current);
mutex_lock(&dev->list_mutex);
w1_process_callbacks(dev); w1_process_callbacks(dev);
mutex_unlock(&dev->list_mutex);
} }
mutex_lock(&dev->list_mutex);
w1_process_callbacks(dev); w1_process_callbacks(dev);
mutex_unlock(&dev->list_mutex);
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.id.mst.id = dev->id; msg.id.mst.id = dev->id;
......
This diff is collapsed.
...@@ -27,6 +27,17 @@ ...@@ -27,6 +27,17 @@
#include "w1.h" #include "w1.h"
/**
* enum w1_cn_msg_flags - bitfield flags for struct cn_msg.flags
*
* @W1_CN_BUNDLE: Request bundling replies into fewer messagse. Be prepared
* to handle multiple struct cn_msg, struct w1_netlink_msg, and
* struct w1_netlink_cmd in one packet.
*/
enum w1_cn_msg_flags {
W1_CN_BUNDLE = 1,
};
/** /**
* enum w1_netlink_message_types - message type * enum w1_netlink_message_types - message type
* *
...@@ -49,6 +60,19 @@ enum w1_netlink_message_types { ...@@ -49,6 +60,19 @@ enum w1_netlink_message_types {
W1_LIST_MASTERS, W1_LIST_MASTERS,
}; };
/**
* struct w1_netlink_msg - holds w1 message type, id, and result
*
* @type: one of enum w1_netlink_message_types
* @status: kernel feedback for success 0 or errno failure value
* @len: length of data following w1_netlink_msg
* @id: union holding master bus id (msg.id) and slave device id (id[8]).
* @data: start address of any following data
*
* The base message structure for w1 messages over netlink.
* The netlink connector data sequence is, struct nlmsghdr, struct cn_msg,
* then one or more struct w1_netlink_msg (each with optional data).
*/
struct w1_netlink_msg struct w1_netlink_msg
{ {
__u8 type; __u8 type;
...@@ -66,6 +90,7 @@ struct w1_netlink_msg ...@@ -66,6 +90,7 @@ struct w1_netlink_msg
/** /**
* enum w1_commands - commands available for master or slave operations * enum w1_commands - commands available for master or slave operations
*
* @W1_CMD_READ: read len bytes * @W1_CMD_READ: read len bytes
* @W1_CMD_WRITE: write len bytes * @W1_CMD_WRITE: write len bytes
* @W1_CMD_SEARCH: initiate a standard search, returns only the slave * @W1_CMD_SEARCH: initiate a standard search, returns only the slave
...@@ -93,6 +118,17 @@ enum w1_commands { ...@@ -93,6 +118,17 @@ enum w1_commands {
W1_CMD_MAX W1_CMD_MAX
}; };
/**
* struct w1_netlink_cmd - holds the command and data
*
* @cmd: one of enum w1_commands
* @res: reserved
* @len: length of data following w1_netlink_cmd
* @data: start address of any following data
*
* One or more struct w1_netlink_cmd is placed starting at w1_netlink_msg.data
* each with optional data.
*/
struct w1_netlink_cmd struct w1_netlink_cmd
{ {
__u8 cmd; __u8 cmd;
......
...@@ -71,6 +71,7 @@ struct cn_dev { ...@@ -71,6 +71,7 @@ struct cn_dev {
int cn_add_callback(struct cb_id *id, const char *name, int cn_add_callback(struct cb_id *id, const char *name,
void (*callback)(struct cn_msg *, struct netlink_skb_parms *)); void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
void cn_del_callback(struct cb_id *); void cn_del_callback(struct cb_id *);
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 group, gfp_t gfp_mask);
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask); int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask);
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name, int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
......
...@@ -185,8 +185,21 @@ struct extcon_specific_cable_nb { ...@@ -185,8 +185,21 @@ struct extcon_specific_cable_nb {
*/ */
extern int extcon_dev_register(struct extcon_dev *edev); extern int extcon_dev_register(struct extcon_dev *edev);
extern void extcon_dev_unregister(struct extcon_dev *edev); extern void extcon_dev_unregister(struct extcon_dev *edev);
extern int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev);
extern void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev);
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
/*
* Following APIs control the memory of extcon device.
*/
extern struct extcon_dev *extcon_dev_allocate(const char **cables);
extern void extcon_dev_free(struct extcon_dev *edev);
extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables);
extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
/* /*
* get/set/update_state access the 32b encoded state value, which represents * get/set/update_state access the 32b encoded state value, which represents
* states of all possible cables of the multistate port. For example, if one * states of all possible cables of the multistate port. For example, if one
...@@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev) ...@@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev)
static inline void extcon_dev_unregister(struct extcon_dev *edev) { } static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
static inline int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev)
{
return -EINVAL;
}
static inline void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev) { }
static inline struct extcon_dev *extcon_dev_allocate(const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void extcon_dev_free(struct extcon_dev *edev) { }
static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void devm_extcon_dev_free(struct extcon_dev *edev) { }
static inline u32 extcon_get_state(struct extcon_dev *edev) static inline u32 extcon_get_state(struct extcon_dev *edev)
{ {
return 0; return 0;
......
...@@ -696,6 +696,8 @@ struct vmbus_channel { ...@@ -696,6 +696,8 @@ struct vmbus_channel {
* preserve the earlier behavior. * preserve the earlier behavior.
*/ */
u32 target_vp; u32 target_vp;
/* The corresponding CPUID in the guest */
u32 target_cpu;
/* /*
* Support for sub-channels. For high performance devices, * Support for sub-channels. For high performance devices,
* it will be useful to have multiple sub-channels to support * it will be useful to have multiple sub-channels to support
...@@ -732,6 +734,11 @@ struct vmbus_channel { ...@@ -732,6 +734,11 @@ struct vmbus_channel {
* Support per-channel state for use by vmbus drivers. * Support per-channel state for use by vmbus drivers.
*/ */
void *per_channel_state; void *per_channel_state;
/*
* To support per-cpu lookup mapping of relid to channel,
* link up channels based on their CPU affinity.
*/
struct list_head percpu_list;
}; };
static inline void set_channel_read_state(struct vmbus_channel *c, bool state) static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/irqreturn.h> #include <linux/irqreturn.h>
struct mcb_driver; struct mcb_driver;
struct mcb_device;
/** /**
* struct mcb_bus - MEN Chameleon Bus * struct mcb_bus - MEN Chameleon Bus
...@@ -23,11 +24,14 @@ struct mcb_driver; ...@@ -23,11 +24,14 @@ struct mcb_driver;
* @dev: pointer to carrier device * @dev: pointer to carrier device
* @children: the child busses * @children: the child busses
* @bus_nr: mcb bus number * @bus_nr: mcb bus number
* @get_irq: callback to get IRQ number
*/ */
struct mcb_bus { struct mcb_bus {
struct list_head children; struct list_head children;
struct device dev; struct device dev;
struct device *carrier;
int bus_nr; int bus_nr;
int (*get_irq)(struct mcb_device *dev);
}; };
#define to_mcb_bus(b) container_of((b), struct mcb_bus, dev) #define to_mcb_bus(b) container_of((b), struct mcb_bus, dev)
...@@ -105,7 +109,7 @@ extern void mcb_unregister_driver(struct mcb_driver *driver); ...@@ -105,7 +109,7 @@ extern void mcb_unregister_driver(struct mcb_driver *driver);
module_driver(__mcb_driver, mcb_register_driver, mcb_unregister_driver); module_driver(__mcb_driver, mcb_register_driver, mcb_unregister_driver);
extern void mcb_bus_add_devices(const struct mcb_bus *bus); extern void mcb_bus_add_devices(const struct mcb_bus *bus);
extern int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev); extern int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev);
extern struct mcb_bus *mcb_alloc_bus(void); extern struct mcb_bus *mcb_alloc_bus(struct device *carrier);
extern struct mcb_bus *mcb_bus_get(struct mcb_bus *bus); extern struct mcb_bus *mcb_bus_get(struct mcb_bus *bus);
extern void mcb_bus_put(struct mcb_bus *bus); extern void mcb_bus_put(struct mcb_bus *bus);
extern struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus); extern struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus);
......
This diff is collapsed.
/* /*
* max14577.h - Driver for the Maxim 14577 * max14577.h - Driver for the Maxim 14577/77836
* *
* Copyright (C) 2013 Samsung Electrnoics * Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com> * Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com> * Krzysztof Kozlowski <k.kozlowski@samsung.com>
* *
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
* MAX14577 has MUIC, Charger devices. * MAX14577 has MUIC, Charger devices.
* The devices share the same I2C bus and interrupt line * The devices share the same I2C bus and interrupt line
* included in this mfd driver. * included in this mfd driver.
*
* MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
* addresses.
*/ */
#ifndef __MAX14577_H__ #ifndef __MAX14577_H__
...@@ -32,7 +35,17 @@ enum max14577_regulators { ...@@ -32,7 +35,17 @@ enum max14577_regulators {
MAX14577_SAFEOUT = 0, MAX14577_SAFEOUT = 0,
MAX14577_CHARGER, MAX14577_CHARGER,
MAX14577_REG_MAX, MAX14577_REGULATOR_NUM,
};
/* MAX77836 regulator IDs */
enum max77836_regulators {
MAX77836_SAFEOUT = 0,
MAX77836_CHARGER,
MAX77836_LDO1,
MAX77836_LDO2,
MAX77836_REGULATOR_NUM,
}; };
struct max14577_regulator_platform_data { struct max14577_regulator_platform_data {
......
...@@ -415,7 +415,7 @@ struct palmas_usb { ...@@ -415,7 +415,7 @@ struct palmas_usb {
struct palmas *palmas; struct palmas *palmas;
struct device *dev; struct device *dev;
struct extcon_dev edev; struct extcon_dev *edev;
int id_otg_irq; int id_otg_irq;
int id_irq; int id_irq;
......
...@@ -64,7 +64,7 @@ struct miscdevice { ...@@ -64,7 +64,7 @@ struct miscdevice {
umode_t mode; umode_t mode;
}; };
extern int misc_register(struct miscdevice * misc); extern int misc_register(struct miscdevice *misc);
extern int misc_deregister(struct miscdevice *misc); extern int misc_deregister(struct miscdevice *misc);
#define MODULE_ALIAS_MISCDEV(minor) \ #define MODULE_ALIAS_MISCDEV(minor) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment