提交 4046136a 编写于 作者: L 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
  ...
......@@ -24,7 +24,8 @@ netlink based networking for inter-process communication in a significantly
easier way:
int cn_add_callback(struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *));
void cn_netlink_send(struct cn_msg *msg, u32 __group, int gfp_mask);
void cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __group, int gfp_mask);
void cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, int gfp_mask);
struct cb_id
{
......@@ -71,15 +72,21 @@ void cn_del_callback(struct cb_id *id);
struct cb_id *id - unique connector's user identifier.
int cn_netlink_send(struct cn_msg *msg, u32 __groups, int gfp_mask);
int cn_netlink_send_multi(struct cn_msg *msg, u16 len, u32 portid, u32 __groups, int gfp_mask);
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __groups, int gfp_mask);
Sends message to the specified groups. It can be safely called from
softirq context, but may silently fail under strong memory pressure.
If there are no listeners for given group -ESRCH can be returned.
struct cn_msg * - message header(with attached data).
u16 len - for *_multi multiple cn_msg messages can be sent
u32 port - destination port.
If non-zero the message will be sent to the
given port, which should be set to the
original sender.
u32 __group - destination group.
If __group is zero, then appropriate group will
If port and __group is zero, then appropriate group will
be searched through all registered connector users,
and message will be delivered to the group which was
created for user with the same ID as in msg.
......@@ -111,7 +118,7 @@ acknowledge number MUST be the same + 1.
If we receive a message and its sequence number is not equal to one we
are expecting, then it is a new message. If we receive a message and
its sequence number is the same as one we are expecting, but its
acknowledge is not equal to the acknowledge number in the original
acknowledge is not equal to the sequence number in the original
message + 1, then it is a new message.
Obviously, the protocol header contains the above id.
......
ARM Versatile Character LCD
-----------------------------------------------------
This binding defines the character LCD interface found on ARM Versatile AB
and PB reference platforms.
Required properties:
- compatible : "arm,versatile-clcd"
- reg : Location and size of character LCD registers
Optional properties:
- interrupts - single interrupt for character LCD. The character LCD can
operate in polled mode without an interrupt.
Example:
lcd@10008000 {
compatible = "arm,versatile-lcd";
reg = <0x10008000 0x1000>;
};
......@@ -26,7 +26,7 @@ Each child node must have one and only one 'reg' entry of type SPMI_USID.
reg = <...>;
#address-cells = <2>;
#size-cells <0>;
#size-cells = <0>;
child@0 {
compatible = "...";
......
......@@ -82,7 +82,7 @@ driver - (standard) symlink to the w1 driver
w1_master_add - Manually register a slave device
w1_master_attempts - the number of times a search was attempted
w1_master_max_slave_count
- the maximum slaves that may be attached to a master
- maximum number of slaves to search for at a time
w1_master_name - the name of the device (w1_bus_masterX)
w1_master_pullup - 5V strong pullup 0 enabled, 1 disabled
w1_master_remove - Manually remove a slave device
......
......@@ -30,7 +30,7 @@ Protocol.
W1_SLAVE_CMD
userspace command for slave device
(read/write/touch)
__u8 res - reserved
__u8 status - error indication from kernel
__u16 len - size of data attached to this header data
union {
__u8 id[8]; - slave unique device id
......@@ -44,10 +44,14 @@ Protocol.
__u8 cmd - command opcode.
W1_CMD_READ - read command
W1_CMD_WRITE - write command
W1_CMD_TOUCH - touch command
(write and sample data back to userspace)
W1_CMD_SEARCH - search command
W1_CMD_ALARM_SEARCH - alarm search command
W1_CMD_TOUCH - touch command
(write and sample data back to userspace)
W1_CMD_RESET - send bus reset
W1_CMD_SLAVE_ADD - add slave to kernel list
W1_CMD_SLAVE_REMOVE - remove slave from kernel list
W1_CMD_LIST_SLAVES - get slaves list from kernel
__u8 res - reserved
__u16 len - length of data for this command
For read command data must be allocated like for write command
......@@ -87,8 +91,7 @@ format:
id0 ... idN
Each message is at most 4k in size, so if number of master devices
exceeds this, it will be split into several messages,
cn.seq will be increased for each one.
exceeds this, it will be split into several messages.
W1 search and alarm search commands.
request:
......
......@@ -2188,6 +2188,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
S: Supported
F: drivers/char/*
F: drivers/misc/*
F: include/linux/miscdevice.h
CHECKPATCH
M: Andy Whitcroft <apw@canonical.com>
......
......@@ -83,7 +83,6 @@ obj-$(CONFIG_PCCARD) += pcmcia/
obj-$(CONFIG_DIO) += dio/
obj-$(CONFIG_SBUS) += sbus/
obj-$(CONFIG_ZORRO) += zorro/
obj-$(CONFIG_MAC) += macintosh/
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
......@@ -141,7 +140,6 @@ obj-y += clk/
obj-$(CONFIG_MAILBOX) += mailbox/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
obj-$(CONFIG_NFC) += nfc/
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-$(CONFIG_RPMSG) += rpmsg/
......
......@@ -345,7 +345,6 @@ static int __init applicom_init(void)
free_irq(apbs[i].irq, &dummy);
iounmap(apbs[i].RamIO);
}
pci_disable_device(dev);
return ret;
}
......
......@@ -43,6 +43,8 @@ static struct cn_dev cdev;
static int cn_already_initialized;
/*
* Sends mult (multiple) cn_msg at a time.
*
* msg->seq and msg->ack are used to determine message genealogy.
* When someone sends message it puts there locally unique sequence
* and random acknowledge numbers. Sequence number may be copied into
......@@ -62,10 +64,13 @@ static int cn_already_initialized;
* the acknowledgement number in the original message + 1, then it is
* a new message.
*
* If msg->len != len, then additional cn_msg messages are expected following
* the first msg.
*
* The message is sent to, the portid if given, the group if given, both if
* both, or if both are zero then the group is looked up and sent there.
*/
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
gfp_t gfp_mask)
{
struct cn_callback_entry *__cbq;
......@@ -98,7 +103,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
if (!portid && !netlink_has_listeners(dev->nls, group))
return -ESRCH;
size = sizeof(*msg) + msg->len;
size = sizeof(*msg) + len;
skb = nlmsg_new(size, gfp_mask);
if (!skb)
......@@ -121,6 +126,14 @@ int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
gfp_mask);
return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT));
}
EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
/* same as cn_netlink_send_mult except msg->len is used for len */
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
gfp_t gfp_mask)
{
return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
}
EXPORT_SYMBOL_GPL(cn_netlink_send);
/*
......
......@@ -28,13 +28,13 @@ config EXTCON_ADC_JACK
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX14577
tristate "MAX14577 EXTCON Support"
tristate "MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577
select IRQ_DOMAIN
select REGMAP_I2C
help
If you say yes here you get support for the MUIC device of
Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory
Maxim MAX14577/77836. The MAX14577/77836 MUIC is a USB port accessory
detector and switch.
config EXTCON_MAX77693
......
......@@ -39,7 +39,7 @@
* @chan: iio channel being queried.
*/
struct adc_jack_data {
struct extcon_dev edev;
struct extcon_dev *edev;
const char **cable_names;
int num_cables;
......@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
ret = iio_read_channel_raw(data->chan, &adc_val);
if (ret < 0) {
dev_err(&data->edev.dev, "read channel() error: %d\n", ret);
dev_err(&data->edev->dev, "read channel() error: %d\n", ret);
return;
}
......@@ -80,7 +80,7 @@ static void adc_jack_handler(struct work_struct *work)
}
/* if no def has met, it means state = 0 (no cables attached) */
extcon_set_state(&data->edev, state);
extcon_set_state(data->edev, state);
}
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
......@@ -102,33 +102,33 @@ static int adc_jack_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
data->edev.name = pdata->name;
if (!pdata->cable_names) {
err = -EINVAL;
dev_err(&pdev->dev, "error: cable_names not defined.\n");
goto out;
return -EINVAL;
}
data->edev.dev.parent = &pdev->dev;
data->edev.supported_cable = pdata->cable_names;
data->edev = devm_extcon_dev_allocate(&pdev->dev, pdata->cable_names);
if (IS_ERR(data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
data->edev->dev.parent = &pdev->dev;
data->edev->name = pdata->name;
/* Check the length of array and set num_cables */
for (i = 0; data->edev.supported_cable[i]; i++)
for (i = 0; data->edev->supported_cable[i]; i++)
;
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
err = -EINVAL;
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
i - 1);
goto out;
return -EINVAL;
}
data->num_cables = i;
if (!pdata->adc_conditions ||
!pdata->adc_conditions[0].state) {
err = -EINVAL;
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
goto out;
return -EINVAL;
}
data->adc_conditions = pdata->adc_conditions;
......@@ -138,10 +138,8 @@ static int adc_jack_probe(struct platform_device *pdev)
data->num_conditions = i;
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
if (IS_ERR(data->chan)) {
err = PTR_ERR(data->chan);
goto out;
}
if (IS_ERR(data->chan))
return PTR_ERR(data->chan);
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
......@@ -149,15 +147,14 @@ static int adc_jack_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
err = extcon_dev_register(&data->edev);
err = devm_extcon_dev_register(&pdev->dev, data->edev);
if (err)
goto out;
return err;
data->irq = platform_get_irq(pdev, 0);
if (!data->irq) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
err = -ENODEV;
goto err_irq;
return -ENODEV;
}
err = request_any_context_irq(data->irq, adc_jack_irq_thread,
......@@ -165,15 +162,10 @@ static int adc_jack_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
goto err_irq;
return err;
}
return 0;
err_irq:
extcon_dev_unregister(&data->edev);
out:
return err;
}
static int adc_jack_remove(struct platform_device *pdev)
......@@ -182,7 +174,6 @@ static int adc_jack_remove(struct platform_device *pdev)
free_irq(data->irq, data);
cancel_work_sync(&data->handler.work);
extcon_dev_unregister(&data->edev);
return 0;
}
......
......@@ -91,7 +91,7 @@ struct arizona_extcon_info {
int hpdet_ip;
struct extcon_dev edev;
struct extcon_dev *edev;
};
static const struct arizona_micd_config micd_default_modes[] = {
......@@ -546,7 +546,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
}
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
......@@ -581,7 +581,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
else
report = ARIZONA_CABLE_HEADPHONE;
ret = extcon_set_cable_state_(&info->edev, report, true);
ret = extcon_set_cable_state_(info->edev, report, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret);
......@@ -664,7 +664,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_update_state(&info->edev,
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0)
......@@ -723,7 +723,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_update_state(&info->edev,
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE);
if (ret != 0)
......@@ -764,7 +764,7 @@ static void arizona_micd_detect(struct work_struct *work)
mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
ret = extcon_get_cable_state_(info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
......@@ -812,7 +812,7 @@ static void arizona_micd_detect(struct work_struct *work)
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
arizona_identify_headphone(info);
ret = extcon_update_state(&info->edev,
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_MICROPHONE,
1 << ARIZONA_CABLE_MICROPHONE);
......@@ -999,7 +999,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (info->last_jackdet == present) {
dev_dbg(arizona->dev, "Detected jack\n");
ret = extcon_set_cable_state_(&info->edev,
ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_MECHANICAL, true);
if (ret != 0)
......@@ -1038,7 +1038,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
info->micd_ranges[i].key, 0);
input_sync(info->input);
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
ret = extcon_update_state(info->edev, 0xffffffff, 0);
if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n",
ret);
......@@ -1105,15 +1105,14 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err;
return -ENOMEM;
}
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
if (IS_ERR(info->micvdd)) {
ret = PTR_ERR(info->micvdd);
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
goto err;
return ret;
}
mutex_init(&info->lock);
......@@ -1151,15 +1150,19 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break;
}
info->edev.name = "Headset Jack";
info->edev.dev.parent = arizona->dev;
info->edev.supported_cable = arizona_cable;
info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
info->edev->name = "Headset Jack";
info->edev->dev.parent = arizona->dev;
ret = extcon_dev_register(&info->edev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret < 0) {
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret);
goto err;
return ret;
}
info->input = devm_input_allocate_device(&pdev->dev);
......@@ -1410,8 +1413,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
err_input:
err_register:
pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
err:
return ret;
}
......@@ -1445,7 +1446,6 @@ static int arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona);
extcon_dev_unregister(&info->edev);
return 0;
}
......
......@@ -565,6 +565,100 @@ static void dummy_sysfs_dev_release(struct device *dev)
{
}
/*
* extcon_dev_allocate() - Allocate the memory of extcon device.
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function allocates the memory for extcon device without allocating
* memory in each extcon provider driver and initialize default setting for
* extcon device.
*
* Return the pointer of extcon device if success or ERR_PTR(err) if fail
*/
struct extcon_dev *extcon_dev_allocate(const char **supported_cable)
{
struct extcon_dev *edev;
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
if (!edev)
return ERR_PTR(-ENOMEM);
edev->max_supported = 0;
edev->supported_cable = supported_cable;
return edev;
}
/*
* extcon_dev_free() - Free the memory of extcon device.
* @edev: the extcon device to free
*/
void extcon_dev_free(struct extcon_dev *edev)
{
kfree(edev);
}
EXPORT_SYMBOL_GPL(extcon_dev_free);
static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
{
struct extcon_dev **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
static void devm_extcon_dev_release(struct device *dev, void *res)
{
extcon_dev_free(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_allocate - Allocate managed extcon device
* @dev: device owning the extcon device being created
* @supported_cable: Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
*
* This function manages automatically the memory of extcon device using device
* resource management and simplify the control of freeing the memory of extcon
* device.
*
* Returns the pointer memory of allocated extcon_dev if success
* or ERR_PTR(err) if fail
*/
struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **supported_cable)
{
struct extcon_dev **ptr, *edev;
ptr = devres_alloc(devm_extcon_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
edev = extcon_dev_allocate(supported_cable);
if (IS_ERR(edev)) {
devres_free(ptr);
return edev;
}
*ptr = edev;
devres_add(dev, ptr);
return edev;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate);
void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_release,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_free);
/**
* extcon_dev_register() - Register a new extcon device
* @edev : the new extcon device (should be allocated before calling)
......@@ -819,6 +913,63 @@ void extcon_dev_unregister(struct extcon_dev *edev)
}
EXPORT_SYMBOL_GPL(extcon_dev_unregister);
static void devm_extcon_dev_unreg(struct device *dev, void *res)
{
extcon_dev_unregister(*(struct extcon_dev **)res);
}
/**
* devm_extcon_dev_register() - Resource-managed extcon_dev_register()
* @dev: device to allocate extcon device
* @edev: the new extcon device to register
*
* Managed extcon_dev_register() function. If extcon device is attached with
* this function, that extcon device is automatically unregistered on driver
* detach. Internally this function calls extcon_dev_register() function.
* To get more information, refer that function.
*
* If extcon device is registered with this function and the device needs to be
* unregistered separately, devm_extcon_dev_unregister() should be used.
*
* Returns 0 if success or negaive error number if failure.
*/
int devm_extcon_dev_register(struct device *dev, struct extcon_dev *edev)
{
struct extcon_dev **ptr;
int ret;
ptr = devres_alloc(devm_extcon_dev_unreg, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = extcon_dev_register(edev);
if (ret) {
devres_free(ptr);
return ret;
}
*ptr = edev;
devres_add(dev, ptr);
return 0;
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_register);
/**
* devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister()
* @dev: device the extcon belongs to
* @edev: the extcon device to unregister
*
* Unregister extcon device that is registered with devm_extcon_dev_register()
* function.
*/
void devm_extcon_dev_unregister(struct device *dev, struct extcon_dev *edev)
{
WARN_ON(devres_release(dev, devm_extcon_dev_unreg,
devm_extcon_dev_match, edev));
}
EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister);
#ifdef CONFIG_OF
/*
* extcon_get_edev_by_phandle - Get the extcon device from devicetree
......
......@@ -32,7 +32,7 @@
#include <linux/extcon/extcon-gpio.h>
struct gpio_extcon_data {
struct extcon_dev edev;
struct extcon_dev *edev;
unsigned gpio;
bool gpio_active_low;
const char *state_on;
......@@ -53,7 +53,7 @@ static void gpio_extcon_work(struct work_struct *work)
state = gpio_get_value(data->gpio);
if (data->gpio_active_low)
state = !state;
extcon_set_state(&data->edev, state);
extcon_set_state(data->edev, state);
}
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
......@@ -67,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
{
struct gpio_extcon_data *extcon_data =
container_of(edev, struct gpio_extcon_data, edev);
struct device *dev = edev->dev.parent;
struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);
const char *state;
if (extcon_get_state(edev))
state = extcon_data->state_on;
else
......@@ -98,15 +99,21 @@ static int gpio_extcon_probe(struct platform_device *pdev)
if (!extcon_data)
return -ENOMEM;
extcon_data->edev.name = pdata->name;
extcon_data->edev.dev.parent = &pdev->dev;
extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL);
if (IS_ERR(extcon_data->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
extcon_data->edev->name = pdata->name;
extcon_data->edev->dev.parent = &pdev->dev;
extcon_data->gpio = pdata->gpio;
extcon_data->gpio_active_low = pdata->gpio_active_low;
extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume;
if (pdata->state_on && pdata->state_off)
extcon_data->edev.print_state = extcon_gpio_print_state;
extcon_data->edev->print_state = extcon_gpio_print_state;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name);
......@@ -121,34 +128,27 @@ static int gpio_extcon_probe(struct platform_device *pdev)
msecs_to_jiffies(pdata->debounce);
}
ret = extcon_dev_register(&extcon_data->edev);
ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);
if (ret < 0)
return ret;
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
if (extcon_data->irq < 0) {
ret = extcon_data->irq;
goto err;
}
if (extcon_data->irq < 0)
return extcon_data->irq;
ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,
pdata->irq_flags, pdev->name,
extcon_data);
if (ret < 0)
goto err;
return ret;
platform_set_drvdata(pdev, extcon_data);
/* Perform initial detection */
gpio_extcon_work(&extcon_data->work.work);
return 0;
err:
extcon_dev_unregister(&extcon_data->edev);
return ret;
}
static int gpio_extcon_remove(struct platform_device *pdev)
......@@ -157,7 +157,6 @@ static int gpio_extcon_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&extcon_data->work);
free_irq(extcon_data->irq, extcon_data);
extcon_dev_unregister(&extcon_data->edev);
return 0;
}
......
/*
* extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC
* extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC
*
* Copyright (C) 2013 Samsung Electrnoics
* Copyright (C) 2013,2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -24,7 +25,6 @@
#include <linux/mfd/max14577-private.h>
#include <linux/extcon.h>
#define DEV_NAME "max14577-muic"
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
enum max14577_muic_adc_debounce_time {
......@@ -40,6 +40,42 @@ enum max14577_muic_status {
MAX14577_MUIC_STATUS_END,
};
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq max14577_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
static struct max14577_muic_irq max77836_muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX77836_IRQ_INT1_ADC1K, "muic-ADC1K" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
{ MAX77836_IRQ_INT2_VIDRM, "muic-VIDRM" },
};
struct max14577_muic_info {
struct device *dev;
struct max14577 *max14577;
......@@ -48,6 +84,8 @@ struct max14577_muic_info {
int prev_chg_type;
u8 status[MAX14577_MUIC_STATUS_END];
struct max14577_muic_irq *muic_irqs;
unsigned int muic_irqs_num;
bool irq_adc;
bool irq_chg;
struct work_struct irq_work;
......@@ -74,29 +112,6 @@ enum max14577_muic_cable_group {
MAX14577_CABLE_GROUP_CHG,
};
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
/* Define supported accessory type */
enum max14577_muic_acc_type {
MAX14577_MUIC_ADC_GROUND = 0x0,
......@@ -528,21 +543,12 @@ static void max14577_muic_irq_work(struct work_struct *work)
return;
}
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
/*
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
* Returns 0 if irq_type does not match registered IRQ for this device type.
*/
static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
{
struct max14577_muic_info *info = data;
int i, irq_type = -1;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
if (irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
switch (irq_type) {
case MAX14577_IRQ_INT1_ADC:
case MAX14577_IRQ_INT1_ADCLOW:
......@@ -550,7 +556,7 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
/* Handle all of accessory except for
type of charger accessory */
info->irq_adc = true;
break;
return 1;
case MAX14577_IRQ_INT2_CHGTYP:
case MAX14577_IRQ_INT2_CHGDETRUN:
case MAX14577_IRQ_INT2_DCDTMR:
......@@ -558,8 +564,62 @@ static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
case MAX14577_IRQ_INT2_VBVOLT:
/* Handle charger accessory */
info->irq_chg = true;
return 1;
default:
return 0;
}
}
/*
* Sets irq_adc or irq_chg in max14577_muic_info and returns 1.
* Returns 0 if irq_type does not match registered IRQ for this device type.
*/
static int max77836_parse_irq(struct max14577_muic_info *info, int irq_type)
{
/* First check common max14577 interrupts */
if (max14577_parse_irq(info, irq_type))
return 1;
switch (irq_type) {
case MAX77836_IRQ_INT1_ADC1K:
info->irq_adc = true;
return 1;
case MAX77836_IRQ_INT2_VIDRM:
/* Handle charger accessory */
info->irq_chg = true;
return 1;
default:
return 0;
}
}
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
{
struct max14577_muic_info *info = data;
int i, irq_type = -1;
bool irq_parsed;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < info->muic_irqs_num; i++)
if (irq == info->muic_irqs[i].virq)
irq_type = info->muic_irqs[i].irq;
switch (info->max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
irq_parsed = max77836_parse_irq(info, irq_type);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
irq_parsed = max14577_parse_irq(info, irq_type);
break;
}
if (!irq_parsed) {
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
irq_type);
return IRQ_HANDLED;
......@@ -644,13 +704,24 @@ static int max14577_muic_probe(struct platform_device *pdev)
INIT_WORK(&info->irq_work, max14577_muic_irq_work);
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
info->muic_irqs = max77836_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max77836_muic_irqs);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
info->muic_irqs = max14577_muic_irqs;
info->muic_irqs_num = ARRAY_SIZE(max14577_muic_irqs);
}
/* Support irq domain for max14577 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
struct max14577_muic_irq *muic_irq = &muic_irqs[i];
for (i = 0; i < info->muic_irqs_num; i++) {
struct max14577_muic_irq *muic_irq = &info->muic_irqs[i];
unsigned int virq = 0;
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
if (!virq)
if (virq <= 0)
return -EINVAL;
muic_irq->virq = virq;
......@@ -668,14 +739,16 @@ static int max14577_muic_probe(struct platform_device *pdev)
}
/* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL);
if (!info->edev) {
info->edev = devm_extcon_dev_allocate(&pdev->dev,
max14577_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM;
}
info->edev->name = DEV_NAME;
info->edev->supported_cable = max14577_extcon_cable;
ret = extcon_dev_register(info->edev);
info->edev->name = dev_name(&pdev->dev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n");
return ret;
......@@ -694,7 +767,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
MAX14577_REG_DEVICEID, &id);
if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon;
return ret;
}
dev_info(info->dev, "device ID : 0x%x\n", id);
......@@ -710,19 +783,10 @@ static int max14577_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer.
*/
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to schedule delayed work for cable detect\n");
goto err_extcon;
}
return ret;
err_extcon:
extcon_dev_unregister(info->edev);
return ret;
}
static int max14577_muic_remove(struct platform_device *pdev)
......@@ -730,23 +794,30 @@ static int max14577_muic_remove(struct platform_device *pdev)
struct max14577_muic_info *info = platform_get_drvdata(pdev);
cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0;
}
static const struct platform_device_id max14577_muic_id[] = {
{ "max14577-muic", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-muic", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_muic_id);
static struct platform_driver max14577_muic_driver = {
.driver = {
.name = DEV_NAME,
.name = "max14577-muic",
.owner = THIS_MODULE,
},
.probe = max14577_muic_probe,
.remove = max14577_muic_remove,
.id_table = max14577_muic_id,
};
module_platform_driver(max14577_muic_driver);
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:extcon-max14577");
......@@ -1175,25 +1175,24 @@ static int max77693_muic_probe(struct platform_device *pdev)
}
/* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
GFP_KERNEL);
if (!info->edev) {
info->edev = devm_extcon_dev_allocate(&pdev->dev,
max77693_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM;
goto err_irq;
}
info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max77693_extcon_cable;
ret = extcon_dev_register(info->edev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq;
}
/* Initialize MUIC register by using platform data or default data */
if (pdata->muic_data) {
if (pdata && pdata->muic_data) {
init_data = pdata->muic_data->init_data;
num_init_data = pdata->muic_data->num_init_data;
} else {
......@@ -1226,7 +1225,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
= init_data[i].data;
}
if (pdata->muic_data) {
if (pdata && pdata->muic_data) {
struct max77693_muic_platform_data *muic_pdata
= pdata->muic_data;
......@@ -1267,7 +1266,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
MAX77693_MUIC_REG_ID, &id);
if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon;
goto err_irq;
}
dev_info(info->dev, "device ID : 0x%x\n", id);
......@@ -1283,12 +1282,11 @@ static int max77693_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer.
*/
INIT_DELAYED_WORK(&info->wq_detcable, max77693_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies);
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return ret;
err_extcon:
extcon_dev_unregister(info->edev);
err_irq:
while (--i >= 0)
free_irq(muic_irqs[i].virq, info);
......@@ -1304,7 +1302,6 @@ static int max77693_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work);
input_unregister_device(info->dock);
extcon_dev_unregister(info->edev);
return 0;
}
......
......@@ -699,23 +699,22 @@ static int max8997_muic_probe(struct platform_device *pdev)
}
/* External connector */
info->edev = devm_kzalloc(&pdev->dev, sizeof(struct extcon_dev),
GFP_KERNEL);
if (!info->edev) {
info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM;
goto err_irq;
}
info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev;
info->edev->supported_cable = max8997_extcon_cable;
ret = extcon_dev_register(info->edev);
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n");
goto err_irq;
}
if (pdata->muic_pdata) {
if (pdata && pdata->muic_pdata) {
struct max8997_muic_platform_data *muic_pdata
= pdata->muic_pdata;
......@@ -770,7 +769,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
* driver should notify cable state to upper layer.
*/
INIT_DELAYED_WORK(&info->wq_detcable, max8997_muic_detect_cable_wq);
schedule_delayed_work(&info->wq_detcable, delay_jiffies);
queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
return 0;
......@@ -789,8 +789,6 @@ static int max8997_muic_remove(struct platform_device *pdev)
free_irq(muic_irqs[i].virq, info);
cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0;
}
......
......@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/mfd/palmas.h>
#include <linux/of.h>
......@@ -56,7 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_cable_state(&palmas_usb->edev, "USB", true);
extcon_set_cable_state(palmas_usb->edev, "USB", true);
dev_info(palmas_usb->dev, "USB cable is attached\n");
} else {
dev_dbg(palmas_usb->dev,
......@@ -65,7 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB", false);
extcon_set_cable_state(palmas_usb->edev, "USB", false);
dev_info(palmas_usb->dev, "USB cable is detached\n");
} else {
dev_dbg(palmas_usb->dev,
......@@ -92,7 +93,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
......@@ -100,17 +101,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
extcon_set_cable_state(palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
}
......@@ -186,13 +187,20 @@ static int palmas_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, palmas_usb);
palmas_usb->edev.supported_cable = palmas_extcon_cable;
palmas_usb->edev.dev.parent = palmas_usb->dev;
palmas_usb->edev.mutually_exclusive = mutually_exclusive;
palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev,
palmas_extcon_cable);
if (IS_ERR(palmas_usb->edev)) {
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
palmas_usb->edev->dev.parent = palmas_usb->dev;
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
status = extcon_dev_register(&palmas_usb->edev);
status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
if (status) {
dev_err(&pdev->dev, "failed to register extcon device\n");
kfree(palmas_usb->edev->name);
return status;
}
......@@ -206,7 +214,8 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->id_irq, status);
goto fail_extcon;
kfree(palmas_usb->edev->name);
return status;
}
}
......@@ -220,25 +229,21 @@ static int palmas_usb_probe(struct platform_device *pdev)
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->vbus_irq, status);
goto fail_extcon;
kfree(palmas_usb->edev->name);
return status;
}
}
palmas_enable_irq(palmas_usb);
device_set_wakeup_capable(&pdev->dev, true);
return 0;
fail_extcon:
extcon_dev_unregister(&palmas_usb->edev);
return status;
}
static int palmas_usb_remove(struct platform_device *pdev)
{
struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
extcon_dev_unregister(&palmas_usb->edev);
kfree(palmas_usb->edev->name);
return 0;
}
......
......@@ -471,18 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
}
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
static void reset_channel_cb(void *arg)
{
struct vmbus_channel *channel = arg;
channel->onchannel_callback = NULL;
}
static void vmbus_close_internal(struct vmbus_channel *channel)
{
struct vmbus_channel_close_channel *msg;
int ret;
unsigned long flags;
channel->state = CHANNEL_OPEN_STATE;
channel->sc_creation_callback = NULL;
/* Stop callback and cancel the timer asap */
spin_lock_irqsave(&channel->inbound_lock, flags);
channel->onchannel_callback = NULL;
spin_unlock_irqrestore(&channel->inbound_lock, flags);
if (channel->target_cpu != smp_processor_id())
smp_call_function_single(channel->target_cpu, reset_channel_cb,
channel, true);
else
reset_channel_cb(channel);
/* Send a closing message */
......@@ -674,8 +682,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
multi_pagebuffer->len);
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT))
if (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)
return -EINVAL;
/*
......
......@@ -149,6 +149,7 @@ static struct vmbus_channel *alloc_channel(void)
spin_lock_init(&channel->sc_lock);
INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
channel->controlwq = create_workqueue("hv_vmbus_ctl");
if (!channel->controlwq) {
......@@ -188,7 +189,20 @@ static void free_channel(struct vmbus_channel *channel)
queue_work(vmbus_connection.work_queue, &channel->work);
}
static void percpu_channel_enq(void *arg)
{
struct vmbus_channel *channel = arg;
int cpu = smp_processor_id();
list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]);
}
static void percpu_channel_deq(void *arg)
{
struct vmbus_channel *channel = arg;
list_del(&channel->percpu_list);
}
/*
* vmbus_process_rescind_offer -
......@@ -210,6 +224,12 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
if (channel->target_cpu != smp_processor_id())
smp_call_function_single(channel->target_cpu,
percpu_channel_deq, channel, true);
else
percpu_channel_deq(channel);
if (channel->primary_channel == NULL) {
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
list_del(&channel->listentry);
......@@ -245,6 +265,7 @@ static void vmbus_process_offer(struct work_struct *work)
work);
struct vmbus_channel *channel;
bool fnew = true;
bool enq = false;
int ret;
unsigned long flags;
......@@ -264,12 +285,22 @@ static void vmbus_process_offer(struct work_struct *work)
}
}
if (fnew)
if (fnew) {
list_add_tail(&newchannel->listentry,
&vmbus_connection.chn_list);
enq = true;
}
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
if (enq) {
if (newchannel->target_cpu != smp_processor_id())
smp_call_function_single(newchannel->target_cpu,
percpu_channel_enq,
newchannel, true);
else
percpu_channel_enq(newchannel);
}
if (!fnew) {
/*
* Check to see if this is a sub-channel.
......@@ -282,6 +313,14 @@ static void vmbus_process_offer(struct work_struct *work)
spin_lock_irqsave(&channel->sc_lock, flags);
list_add_tail(&newchannel->sc_list, &channel->sc_list);
spin_unlock_irqrestore(&channel->sc_lock, flags);
if (newchannel->target_cpu != smp_processor_id())
smp_call_function_single(newchannel->target_cpu,
percpu_channel_enq,
newchannel, true);
else
percpu_channel_enq(newchannel);
newchannel->state = CHANNEL_OPEN_STATE;
if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel);
......@@ -365,7 +404,7 @@ static u32 next_vp;
* performance critical channels (IDE, SCSI and Network) will be uniformly
* distributed across all available CPUs.
*/
static u32 get_vp_index(uuid_le *type_guid)
static void init_vp_index(struct vmbus_channel *channel, uuid_le *type_guid)
{
u32 cur_cpu;
int i;
......@@ -387,10 +426,13 @@ static u32 get_vp_index(uuid_le *type_guid)
* Also if the channel is not a performance critical
* channel, bind it to cpu 0.
*/
return 0;
channel->target_cpu = 0;
channel->target_vp = 0;
return;
}
cur_cpu = (++next_vp % max_cpus);
return hv_context.vp_index[cur_cpu];
channel->target_cpu = cur_cpu;
channel->target_vp = hv_context.vp_index[cur_cpu];
}
/*
......@@ -438,7 +480,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
offer->connection_id;
}
newchannel->target_vp = get_vp_index(&offer->offer.if_type);
init_vp_index(newchannel, &offer->offer.if_type);
memcpy(&newchannel->offermsg, offer,
sizeof(struct vmbus_channel_offer_channel));
......
......@@ -224,8 +224,8 @@ int vmbus_connect(void)
vmbus_connection.int_page = NULL;
}
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0);
free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
vmbus_connection.monitor_pages[0] = NULL;
vmbus_connection.monitor_pages[1] = NULL;
......@@ -234,6 +234,28 @@ int vmbus_connect(void)
return ret;
}
/*
* Map the given relid to the corresponding channel based on the
* per-cpu list of channels that have been affinitized to this CPU.
* This will be used in the channel callback path as we can do this
* mapping in a lock-free fashion.
*/
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
{
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
int cpu = smp_processor_id();
struct list_head *pcpu_head = &hv_context.percpu_list[cpu];
list_for_each_entry(channel, pcpu_head, percpu_list) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
}
}
return found_channel;
}
/*
* relid2channel - Get the channel object given its
......@@ -277,7 +299,6 @@ struct vmbus_channel *relid2channel(u32 relid)
static void process_chn_event(u32 relid)
{
struct vmbus_channel *channel;
unsigned long flags;
void *arg;
bool read_state;
u32 bytes_to_read;
......@@ -286,7 +307,7 @@ static void process_chn_event(u32 relid)
* Find the channel based on this relid and invokes the
* channel callback to process the event
*/
channel = relid2channel(relid);
channel = pcpu_relid2channel(relid);
if (!channel) {
pr_err("channel not found for relid - %u\n", relid);
......@@ -296,13 +317,12 @@ static void process_chn_event(u32 relid)
/*
* A channel once created is persistent even when there
* is no driver handling the device. An unloading driver
* sets the onchannel_callback to NULL under the
* protection of the channel inbound_lock. Thus, checking
* and invoking the driver specific callback takes care of
* orderly unloading of the driver.
* sets the onchannel_callback to NULL on the same CPU
* as where this interrupt is handled (in an interrupt context).
* Thus, checking and invoking the driver specific callback takes
* care of orderly unloading of the driver.
*/
spin_lock_irqsave(&channel->inbound_lock, flags);
if (channel->onchannel_callback != NULL) {
arg = channel->channel_callback_context;
read_state = channel->batched_reading;
......@@ -327,7 +347,6 @@ static void process_chn_event(u32 relid)
pr_err("no channel callback for relid - %u\n", relid);
}
spin_unlock_irqrestore(&channel->inbound_lock, flags);
}
/*
......
......@@ -383,6 +383,8 @@ void hv_synic_init(void *arg)
*/
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_context.vp_index[cpu] = (u32)vp_index;
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
return;
}
......
......@@ -19,6 +19,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/mman.h>
#include <linux/delay.h>
#include <linux/init.h>
......@@ -459,6 +460,11 @@ static bool do_hot_add;
*/
static uint pressure_report_delay = 45;
/*
* The last time we posted a pressure report to host.
*/
static unsigned long last_post_time;
module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
......@@ -542,6 +548,7 @@ struct hv_dynmem_device {
static struct hv_dynmem_device dm_device;
static void post_status(struct hv_dynmem_device *dm);
#ifdef CONFIG_MEMORY_HOTPLUG
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
......@@ -612,7 +619,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
* have not been "onlined" within the allowed time.
*/
wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
post_status(&dm_device);
}
return;
......@@ -951,11 +958,17 @@ static void post_status(struct hv_dynmem_device *dm)
{
struct dm_status status;
struct sysinfo val;
unsigned long now = jiffies;
unsigned long last_post = last_post_time;
if (pressure_report_delay > 0) {
--pressure_report_delay;
return;
}
if (!time_after(now, (last_post_time + HZ)))
return;
si_meminfo(&val);
memset(&status, 0, sizeof(struct dm_status));
status.hdr.type = DM_STATUS_REPORT;
......@@ -983,6 +996,14 @@ static void post_status(struct hv_dynmem_device *dm)
if (status.hdr.trans_id != atomic_read(&trans_id))
return;
/*
* If the last post time that we sampled has changed,
* we have raced, don't post the status.
*/
if (last_post != last_post_time)
return;
last_post_time = jiffies;
vmbus_sendpacket(dm->dev->channel, &status,
sizeof(struct dm_status),
(unsigned long)NULL,
......@@ -1117,7 +1138,7 @@ static void balloon_up(struct work_struct *dummy)
if (ret == -EAGAIN)
msleep(20);
post_status(&dm_device);
} while (ret == -EAGAIN);
if (ret) {
......@@ -1144,8 +1165,10 @@ static void balloon_down(struct hv_dynmem_device *dm,
struct dm_unballoon_response resp;
int i;
for (i = 0; i < range_count; i++)
for (i = 0; i < range_count; i++) {
free_balloon_pages(dm, &range_array[i]);
post_status(&dm_device);
}
if (req->more_pages == 1)
return;
......
......@@ -510,6 +510,11 @@ struct hv_context {
* basis.
*/
struct tasklet_struct *event_dpc[NR_CPUS];
/*
* To optimize the mapping of relid to channel, maintain
* per-cpu list of the channels based on their CPU affinity.
*/
struct list_head percpu_list[NR_CPUS];
};
extern struct hv_context hv_context;
......
......@@ -183,14 +183,14 @@ EXPORT_SYMBOL_GPL(mcb_device_register);
*
* Allocate a new @mcb_bus.
*/
struct mcb_bus *mcb_alloc_bus(void)
struct mcb_bus *mcb_alloc_bus(struct device *carrier)
{
struct mcb_bus *bus;
int bus_nr;
bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL);
if (!bus)
return NULL;
return ERR_PTR(-ENOMEM);
bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL);
if (bus_nr < 0) {
......@@ -200,7 +200,7 @@ struct mcb_bus *mcb_alloc_bus(void)
INIT_LIST_HEAD(&bus->children);
bus->bus_nr = bus_nr;
bus->carrier = carrier;
return bus;
}
EXPORT_SYMBOL_GPL(mcb_alloc_bus);
......@@ -378,6 +378,13 @@ void mcb_release_mem(struct resource *mem)
}
EXPORT_SYMBOL_GPL(mcb_release_mem);
static int __mcb_get_irq(struct mcb_device *dev)
{
struct resource *irq = &dev->irq;
return irq->start;
}
/**
* mcb_get_irq() - Get device's IRQ number
* @dev: The @mcb_device the IRQ is for
......@@ -386,9 +393,12 @@ EXPORT_SYMBOL_GPL(mcb_release_mem);
*/
int mcb_get_irq(struct mcb_device *dev)
{
struct resource *irq = &dev->irq;
struct mcb_bus *bus = dev->bus;
return irq->start;
if (bus->get_irq)
return bus->get_irq(dev);
return __mcb_get_irq(dev);
}
EXPORT_SYMBOL_GPL(mcb_get_irq);
......
......@@ -20,6 +20,15 @@ struct priv {
void __iomem *base;
};
static int mcb_pci_get_irq(struct mcb_device *mdev)
{
struct mcb_bus *mbus = mdev->bus;
struct device *dev = mbus->carrier;
struct pci_dev *pdev = to_pci_dev(dev);
return pdev->irq;
}
static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct priv *priv;
......@@ -67,7 +76,13 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pci_set_drvdata(pdev, priv);
priv->bus = mcb_alloc_bus();
priv->bus = mcb_alloc_bus(&pdev->dev);
if (IS_ERR(priv->bus)) {
ret = PTR_ERR(priv->bus);
goto err_drvdata;
}
priv->bus->get_irq = mcb_pci_get_irq;
ret = chameleon_parse_cells(priv->bus, mapbase, priv->base);
if (ret < 0)
......
......@@ -331,15 +331,15 @@ config MFD_88PM860X
battery-charger under the corresponding menus.
config MFD_MAX14577
bool "Maxim Semiconductor MAX14577 MUIC + Charger Support"
bool "Maxim Semiconductor MAX14577/77836 MUIC + Charger Support"
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
select IRQ_DOMAIN
help
Say yes here to add support for Maxim Semiconductor MAX14577.
This is a Micro-USB IC with Charger controls on chip.
Say yes here to add support for Maxim Semiconductor MAX14577 and
MAX77836 Micro-USB ICs with battery charger.
This driver provides common support for accessing the device;
additional drivers must be enabled in order to use the functionality
of the device.
......
/*
* max14577.c - mfd core driver for the Maxim 14577
* max14577.c - mfd core driver for the Maxim 14577/77836
*
* Copyright (C) 2013 Samsung Electrnoics
* Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
......@@ -21,6 +21,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h>
......@@ -37,7 +38,38 @@ static struct mfd_cell max14577_devs[] = {
{ .name = "max14577-charger", },
};
static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
static struct mfd_cell max77836_devs[] = {
{
.name = "max77836-muic",
.of_compatible = "maxim,max77836-muic",
},
{
.name = "max77836-regulator",
.of_compatible = "maxim,max77836-regulator",
},
{
.name = "max77836-charger",
.of_compatible = "maxim,max77836-charger",
},
{
.name = "max77836-battery",
.of_compatible = "maxim,max77836-battery",
},
};
static struct of_device_id max14577_dt_match[] = {
{
.compatible = "maxim,max14577",
.data = (void *)MAXIM_DEVICE_TYPE_MAX14577,
},
{
.compatible = "maxim,max77836",
.data = (void *)MAXIM_DEVICE_TYPE_MAX77836,
},
{},
};
static bool max14577_muic_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX14577_REG_INT1 ... MAX14577_REG_STATUS3:
......@@ -48,49 +80,221 @@ static bool max14577_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
static const struct regmap_config max14577_regmap_config = {
static bool max77836_muic_volatile_reg(struct device *dev, unsigned int reg)
{
/* Any max14577 volatile registers are also max77836 volatile. */
if (max14577_muic_volatile_reg(dev, reg))
return true;
switch (reg) {
case MAX77836_FG_REG_VCELL_MSB ... MAX77836_FG_REG_SOC_LSB:
case MAX77836_FG_REG_CRATE_MSB ... MAX77836_FG_REG_CRATE_LSB:
case MAX77836_FG_REG_STATUS_H ... MAX77836_FG_REG_STATUS_L:
case MAX77836_PMIC_REG_INTSRC:
case MAX77836_PMIC_REG_TOPSYS_INT:
case MAX77836_PMIC_REG_TOPSYS_STAT:
return true;
default:
break;
}
return false;
}
static const struct regmap_config max14577_muic_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = max14577_volatile_reg,
.volatile_reg = max14577_muic_volatile_reg,
.max_register = MAX14577_REG_END,
};
static const struct regmap_config max77836_pmic_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = max77836_muic_volatile_reg,
.max_register = MAX77836_PMIC_REG_END,
};
static const struct regmap_irq max14577_irqs[] = {
/* INT1 interrupts */
{ .reg_offset = 0, .mask = INT1_ADC_MASK, },
{ .reg_offset = 0, .mask = INT1_ADCLOW_MASK, },
{ .reg_offset = 0, .mask = INT1_ADCERR_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
/* INT2 interrupts */
{ .reg_offset = 1, .mask = INT2_CHGTYP_MASK, },
{ .reg_offset = 1, .mask = INT2_CHGDETRUN_MASK, },
{ .reg_offset = 1, .mask = INT2_DCDTMR_MASK, },
{ .reg_offset = 1, .mask = INT2_DBCHG_MASK, },
{ .reg_offset = 1, .mask = INT2_VBVOLT_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
/* INT3 interrupts */
{ .reg_offset = 2, .mask = INT3_EOC_MASK, },
{ .reg_offset = 2, .mask = INT3_CGMBC_MASK, },
{ .reg_offset = 2, .mask = INT3_OVP_MASK, },
{ .reg_offset = 2, .mask = INT3_MBCCHGERR_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
};
static const struct regmap_irq_chip max14577_irq_chip = {
.name = "max14577",
.status_base = MAX14577_REG_INT1,
.mask_base = MAX14577_REG_INTMASK1,
.mask_invert = 1,
.mask_invert = true,
.num_regs = 3,
.irqs = max14577_irqs,
.num_irqs = ARRAY_SIZE(max14577_irqs),
};
static const struct regmap_irq max77836_muic_irqs[] = {
/* INT1 interrupts */
{ .reg_offset = 0, .mask = MAX14577_INT1_ADC_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCLOW_MASK, },
{ .reg_offset = 0, .mask = MAX14577_INT1_ADCERR_MASK, },
{ .reg_offset = 0, .mask = MAX77836_INT1_ADC1K_MASK, },
/* INT2 interrupts */
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGTYP_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_CHGDETRUN_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DCDTMR_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_DBCHG_MASK, },
{ .reg_offset = 1, .mask = MAX14577_INT2_VBVOLT_MASK, },
{ .reg_offset = 1, .mask = MAX77836_INT2_VIDRM_MASK, },
/* INT3 interrupts */
{ .reg_offset = 2, .mask = MAX14577_INT3_EOC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_CGMBC_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_OVP_MASK, },
{ .reg_offset = 2, .mask = MAX14577_INT3_MBCCHGERR_MASK, },
};
static const struct regmap_irq_chip max77836_muic_irq_chip = {
.name = "max77836-muic",
.status_base = MAX14577_REG_INT1,
.mask_base = MAX14577_REG_INTMASK1,
.mask_invert = true,
.num_regs = 3,
.irqs = max77836_muic_irqs,
.num_irqs = ARRAY_SIZE(max77836_muic_irqs),
};
static const struct regmap_irq max77836_pmic_irqs[] = {
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T120C_MASK, },
{ .reg_offset = 0, .mask = MAX77836_TOPSYS_INT_T140C_MASK, },
};
static const struct regmap_irq_chip max77836_pmic_irq_chip = {
.name = "max77836-pmic",
.status_base = MAX77836_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77836_PMIC_REG_TOPSYS_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77836_pmic_irqs,
.num_irqs = ARRAY_SIZE(max77836_pmic_irqs),
};
static void max14577_print_dev_type(struct max14577 *max14577)
{
u8 reg_data, vendor_id, device_id;
int ret;
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
&reg_data);
if (ret) {
dev_err(max14577->dev,
"Failed to read DEVICEID register: %d\n", ret);
return;
}
vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
DEVID_VENDORID_SHIFT);
device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
DEVID_DEVICEID_SHIFT);
dev_info(max14577->dev, "Device type: %u (ID: 0x%x, vendor: 0x%x)\n",
max14577->dev_type, device_id, vendor_id);
}
/*
* Max77836 specific initialization code for driver probe.
* Adds new I2C dummy device, regmap and regmap IRQ chip.
* Unmasks Interrupt Source register.
*
* On success returns 0.
* On failure returns errno and reverts any changes done so far (e.g. remove
* I2C dummy device), except masking the INT SRC register.
*/
static int max77836_init(struct max14577 *max14577)
{
int ret;
u8 intsrc_mask;
max14577->i2c_pmic = i2c_new_dummy(max14577->i2c->adapter,
I2C_ADDR_PMIC);
if (!max14577->i2c_pmic) {
dev_err(max14577->dev, "Failed to register PMIC I2C device\n");
return -ENODEV;
}
i2c_set_clientdata(max14577->i2c_pmic, max14577);
max14577->regmap_pmic = devm_regmap_init_i2c(max14577->i2c_pmic,
&max77836_pmic_regmap_config);
if (IS_ERR(max14577->regmap_pmic)) {
ret = PTR_ERR(max14577->regmap_pmic);
dev_err(max14577->dev, "Failed to allocate PMIC register map: %d\n",
ret);
goto err;
}
/* Un-mask MAX77836 Interrupt Source register */
ret = max14577_read_reg(max14577->regmap_pmic,
MAX77836_PMIC_REG_INTSRC_MASK, &intsrc_mask);
if (ret < 0) {
dev_err(max14577->dev, "Failed to read PMIC register\n");
goto err;
}
intsrc_mask &= ~(MAX77836_INTSRC_MASK_TOP_INT_MASK);
intsrc_mask &= ~(MAX77836_INTSRC_MASK_MUIC_CHG_INT_MASK);
ret = max14577_write_reg(max14577->regmap_pmic,
MAX77836_PMIC_REG_INTSRC_MASK, intsrc_mask);
if (ret < 0) {
dev_err(max14577->dev, "Failed to write PMIC register\n");
goto err;
}
ret = regmap_add_irq_chip(max14577->regmap_pmic, max14577->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
0, &max77836_pmic_irq_chip,
&max14577->irq_data_pmic);
if (ret != 0) {
dev_err(max14577->dev, "Failed to request PMIC IRQ %d: %d\n",
max14577->irq, ret);
goto err;
}
return 0;
err:
i2c_unregister_device(max14577->i2c_pmic);
return ret;
}
/*
* Max77836 specific de-initialization code for driver remove.
*/
static void max77836_remove(struct max14577 *max14577)
{
regmap_del_irq_chip(max14577->irq, max14577->irq_data_pmic);
i2c_unregister_device(max14577->i2c_pmic);
}
static int max14577_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max14577 *max14577;
struct max14577_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct device_node *np = i2c->dev.of_node;
u8 reg_data;
int ret = 0;
const struct regmap_irq_chip *irq_chip;
struct mfd_cell *mfd_devs;
unsigned int mfd_devs_size;
int irq_flags;
if (np) {
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
......@@ -113,7 +317,8 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
max14577->i2c = i2c;
max14577->irq = i2c->irq;
max14577->regmap = devm_regmap_init_i2c(i2c, &max14577_regmap_config);
max14577->regmap = devm_regmap_init_i2c(i2c,
&max14577_muic_regmap_config);
if (IS_ERR(max14577->regmap)) {
ret = PTR_ERR(max14577->regmap);
dev_err(max14577->dev, "Failed to allocate register map: %d\n",
......@@ -121,23 +326,36 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret;
}
ret = max14577_read_reg(max14577->regmap, MAX14577_REG_DEVICEID,
&reg_data);
if (ret) {
dev_err(max14577->dev, "Device not found on this channel: %d\n",
ret);
return ret;
if (np) {
const struct of_device_id *of_id;
of_id = of_match_device(max14577_dt_match, &i2c->dev);
if (of_id)
max14577->dev_type = (unsigned int)of_id->data;
} else {
max14577->dev_type = id->driver_data;
}
max14577_print_dev_type(max14577);
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
irq_chip = &max77836_muic_irq_chip;
mfd_devs = max77836_devs;
mfd_devs_size = ARRAY_SIZE(max77836_devs);
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
irq_chip = &max14577_irq_chip;
mfd_devs = max14577_devs;
mfd_devs_size = ARRAY_SIZE(max14577_devs);
irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
break;
}
max14577->vendor_id = ((reg_data & DEVID_VENDORID_MASK) >>
DEVID_VENDORID_SHIFT);
max14577->device_id = ((reg_data & DEVID_DEVICEID_MASK) >>
DEVID_DEVICEID_SHIFT);
dev_info(max14577->dev, "Device ID: 0x%x, vendor: 0x%x\n",
max14577->device_id, max14577->vendor_id);
ret = regmap_add_irq_chip(max14577->regmap, max14577->irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
&max14577_irq_chip,
irq_flags, 0, irq_chip,
&max14577->irq_data);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n",
......@@ -145,8 +363,15 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return ret;
}
ret = mfd_add_devices(max14577->dev, -1, max14577_devs,
ARRAY_SIZE(max14577_devs), NULL, 0,
/* Max77836 specific initialization code (additional regmap) */
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836) {
ret = max77836_init(max14577);
if (ret < 0)
goto err_max77836;
}
ret = mfd_add_devices(max14577->dev, -1, mfd_devs,
mfd_devs_size, NULL, 0,
regmap_irq_get_domain(max14577->irq_data));
if (ret < 0)
goto err_mfd;
......@@ -156,6 +381,9 @@ static int max14577_i2c_probe(struct i2c_client *i2c,
return 0;
err_mfd:
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
max77836_remove(max14577);
err_max77836:
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
return ret;
......@@ -167,12 +395,15 @@ static int max14577_i2c_remove(struct i2c_client *i2c)
mfd_remove_devices(max14577->dev);
regmap_del_irq_chip(max14577->irq, max14577->irq_data);
if (max14577->dev_type == MAXIM_DEVICE_TYPE_MAX77836)
max77836_remove(max14577);
return 0;
}
static const struct i2c_device_id max14577_i2c_id[] = {
{ "max14577", 0 },
{ "max14577", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(i2c, max14577_i2c_id);
......@@ -215,11 +446,6 @@ static int max14577_resume(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
static struct of_device_id max14577_dt_match[] = {
{ .compatible = "maxim,max14577", },
{},
};
static SIMPLE_DEV_PM_OPS(max14577_pm, max14577_suspend, max14577_resume);
static struct i2c_driver max14577_i2c_driver = {
......@@ -236,6 +462,9 @@ static struct i2c_driver max14577_i2c_driver = {
static int __init max14577_i2c_init(void)
{
BUILD_BUG_ON(ARRAY_SIZE(max14577_i2c_id) != MAXIM_DEVICE_TYPE_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max14577_dt_match) != MAXIM_DEVICE_TYPE_NUM);
return i2c_add_driver(&max14577_i2c_driver);
}
subsys_initcall(max14577_i2c_init);
......@@ -247,5 +476,5 @@ static void __exit max14577_i2c_exit(void)
module_exit(max14577_i2c_exit);
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("MAXIM 14577 multi-function core driver");
MODULE_DESCRIPTION("Maxim 14577/77836 multi-function core driver");
MODULE_LICENSE("GPL");
......@@ -54,6 +54,7 @@ config AD525X_DPOT_SPI
config ATMEL_PWM
tristate "Atmel AT32/AT91 PWM support"
depends on HAVE_CLK
depends on AVR32 || AT91SAM9263 || AT91SAM9RL || AT91SAM9G45
help
This option enables device driver support for the PWM channels
on certain Atmel processors. Pulse Width Modulation is used for
......@@ -200,7 +201,7 @@ config ICS932S401
config ATMEL_SSC
tristate "Device driver for Atmel SSC peripheral"
depends on HAS_IOMEM
depends on HAS_IOMEM && (AVR32 || ARCH_AT91 || COMPILE_TEST)
---help---
This option enables device driver support for Atmel Synchronized
Serial Communication peripheral (SSC).
......@@ -468,7 +469,7 @@ config BMP085_SPI
config PCH_PHUB
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
select GENERIC_NET_UTILS
depends on PCI
depends on PCI && (X86_32 || COMPILE_TEST)
help
This driver is for PCH(Platform controller Hub) PHUB(Packet Hub) of
Intel Topcliff which is an IOH(Input/Output Hub) for x86 embedded
......
......@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/io.h>
......@@ -366,11 +367,17 @@ static const struct dev_pm_ops charlcd_pm_ops = {
.resume = charlcd_resume,
};
static const struct of_device_id charlcd_match[] = {
{ .compatible = "arm,versatile-lcd", },
{}
};
static struct platform_driver charlcd_driver = {
.driver = {
.name = DRIVERNAME,
.owner = THIS_MODULE,
.pm = &charlcd_pm_ops,
.of_match_table = of_match_ptr(charlcd_match),
},
.remove = __exit_p(charlcd_remove),
};
......
......@@ -85,7 +85,6 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct i2c_client *client = to_i2c_client(dev);
char *endp;
u64 val;
__le32 val_le;
int rc;
......@@ -93,8 +92,8 @@ static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
/* Decode input */
val = simple_strtoull(buf, &endp, 0);
if (buf == endp) {
rc = kstrtoull(buf, 0, &val);
if (rc < 0) {
dev_dbg(dev, "input string not a number\n");
return -EINVAL;
}
......
......@@ -348,7 +348,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
char name[64];
unsigned int i;
sprintf(card_name, "%s%u_card", GENWQE_DEVNAME, cd->card_idx);
sprintf(card_name, "%s%d_card", GENWQE_DEVNAME, cd->card_idx);
root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
if (!root) {
......@@ -454,7 +454,7 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
}
for (i = 0; i < GENWQE_MAX_VFS; i++) {
sprintf(name, "vf%d_jobtimeout_msec", i);
sprintf(name, "vf%u_jobtimeout_msec", i);
file = debugfs_create_u32(name, 0666, root,
&cd->vf_jobtimeout_msec[i]);
......
......@@ -454,7 +454,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl,
*/
int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl)
{
int rc;
int rc = 0;
struct pci_dev *pci_dev = cd->pci_dev;
if (sgl->fpage) {
......
......@@ -111,8 +111,6 @@ int mei_amthif_host_init(struct mei_device *dev)
return ret;
}
cl->state = MEI_FILE_CONNECTING;
ret = mei_cl_connect(cl, NULL);
dev->iamthif_state = MEI_IAMTHIF_IDLE;
......
......@@ -247,7 +247,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
return id;
if (length > dev->me_clients[id].props.max_msg_length)
return -EINVAL;
return -EFBIG;
cb = mei_io_cb_init(cl, NULL);
if (!cb)
......@@ -427,8 +427,6 @@ int mei_cl_enable_device(struct mei_cl_device *device)
mutex_lock(&dev->device_lock);
cl->state = MEI_FILE_CONNECTING;
err = mei_cl_connect(cl, NULL);
if (err < 0) {
mutex_unlock(&dev->device_lock);
......
......@@ -18,6 +18,7 @@
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h>
......@@ -415,6 +416,10 @@ void mei_host_client_init(struct work_struct *work)
dev->reset_count = 0;
mutex_unlock(&dev->device_lock);
pm_runtime_mark_last_busy(&dev->pdev->dev);
dev_dbg(&dev->pdev->dev, "rpm: autosuspend\n");
pm_runtime_autosuspend(&dev->pdev->dev);
}
/**
......@@ -425,6 +430,12 @@ void mei_host_client_init(struct work_struct *work)
*/
bool mei_hbuf_acquire(struct mei_device *dev)
{
if (mei_pg_state(dev) == MEI_PG_ON ||
dev->pg_event == MEI_PG_EVENT_WAIT) {
dev_dbg(&dev->pdev->dev, "device is in pg\n");
return false;
}
if (!dev->hbuf_is_ready) {
dev_dbg(&dev->pdev->dev, "hbuf is not ready\n");
return false;
......@@ -460,9 +471,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
if (cl->state != MEI_FILE_DISCONNECTING)
return 0;
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, NULL);
if (!cb)
return -ENOMEM;
if (!cb) {
rets = -ENOMEM;
goto free;
}
cb->fop_type = MEI_FOP_CLOSE;
if (mei_hbuf_acquire(dev)) {
......@@ -494,8 +514,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
cl_err(dev, cl, "wrong status client disconnect.\n");
if (err)
cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
err);
cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
cl_err(dev, cl, "failed to disconnect from FW client.\n");
}
......@@ -503,6 +522,10 @@ int mei_cl_disconnect(struct mei_cl *cl)
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
free:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
mei_io_cb_free(cb);
return rets;
}
......@@ -557,6 +580,13 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
dev = cl->dev;
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, file);
if (!cb) {
rets = -ENOMEM;
......@@ -567,6 +597,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
/* run hbuf acquire last so we don't have to undo */
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
cl->state = MEI_FILE_CONNECTING;
if (mei_hbm_cl_connect_req(dev, cl)) {
rets = -ENODEV;
goto out;
......@@ -596,6 +627,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
rets = cl->status;
out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
mei_io_cb_free(cb);
return rets;
}
......@@ -713,23 +748,31 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
return -ENOTTY;
}
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb = mei_io_cb_init(cl, NULL);
if (!cb)
return -ENOMEM;
if (!cb) {
rets = -ENOMEM;
goto out;
}
/* always allocate at least client max message */
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
rets = mei_io_cb_alloc_resp_buf(cb, length);
if (rets)
goto err;
goto out;
cb->fop_type = MEI_FOP_READ;
if (mei_hbuf_acquire(dev)) {
if (mei_hbm_cl_flow_control_req(dev, cl)) {
cl_err(dev, cl, "flow control send failed\n");
rets = -ENODEV;
goto err;
}
rets = mei_hbm_cl_flow_control_req(dev, cl);
if (rets < 0)
goto out;
list_add_tail(&cb->list, &dev->read_list.list);
} else {
list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
......@@ -737,9 +780,14 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
cl->read_cb = cb;
return rets;
err:
mei_io_cb_free(cb);
out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
if (rets)
mei_io_cb_free(cb);
return rets;
}
......@@ -776,7 +824,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return rets;
if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
return 0;
}
......@@ -856,6 +904,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
rets = pm_runtime_get(&dev->pdev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(&dev->pdev->dev);
cl_err(dev, cl, "rpm: get failed %d\n", rets);
return rets;
}
cb->fop_type = MEI_FOP_WRITE;
cb->buf_idx = 0;
......@@ -926,6 +980,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
rets = buf->size;
err:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(&dev->pdev->dev);
pm_runtime_put_autosuspend(&dev->pdev->dev);
return rets;
}
......
......@@ -14,10 +14,12 @@
*
*/
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mei.h>
#include <linux/pm_runtime.h>
#include "mei_dev.h"
#include "hbm.h"
......@@ -57,6 +59,34 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status)
}
}
/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
*/
void mei_hbm_idle(struct mei_device *dev)
{
dev->init_clients_timer = 0;
dev->hbm_state = MEI_HBM_IDLE;
}
/**
* mei_hbm_reset - reset hbm counters and book keeping data structurs
*
* @dev: the device structure
*/
void mei_hbm_reset(struct mei_device *dev)
{
dev->me_clients_num = 0;
dev->me_client_presentation_num = 0;
dev->me_client_index = 0;
kfree(dev->me_clients);
dev->me_clients = NULL;
mei_hbm_idle(dev);
}
/**
* mei_hbm_me_cl_allocate - allocates storage for me clients
*
......@@ -69,9 +99,7 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
struct mei_me_client *clients;
int b;
dev->me_clients_num = 0;
dev->me_client_presentation_num = 0;
dev->me_client_index = 0;
mei_hbm_reset(dev);
/* count how many ME clients we have */
for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
......@@ -80,9 +108,6 @@ static int mei_hbm_me_cl_allocate(struct mei_device *dev)
if (dev->me_clients_num == 0)
return 0;
kfree(dev->me_clients);
dev->me_clients = NULL;
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
dev->me_clients_num * sizeof(struct mei_me_client));
/* allocate storage for ME clients representation */
......@@ -133,17 +158,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
}
/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
*/
void mei_hbm_idle(struct mei_device *dev)
{
dev->init_clients_timer = 0;
dev->hbm_state = MEI_HBM_IDLE;
}
int mei_hbm_start_wait(struct mei_device *dev)
{
int ret;
......@@ -289,6 +303,34 @@ static int mei_hbm_prop_req(struct mei_device *dev)
return 0;
}
/*
* mei_hbm_pg - sends pg command
*
* @dev: the device structure
* @pg_cmd: the pg command code
*
* This function returns -EIO on write failure
*/
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd)
{
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
struct hbm_power_gate *req;
const size_t len = sizeof(struct hbm_power_gate);
int ret;
mei_hbm_hdr(mei_hdr, len);
req = (struct hbm_power_gate *)dev->wr_msg.data;
memset(req, 0, len);
req->hbm_cmd = pg_cmd;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret)
dev_err(&dev->pdev->dev, "power gate command write failed.\n");
return ret;
}
EXPORT_SYMBOL_GPL(mei_hbm_pg);
/**
* mei_hbm_stop_req - send stop request message
*
......@@ -701,6 +743,27 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
mei_hbm_cl_flow_control_res(dev, flow_control);
break;
case MEI_PG_ISOLATION_ENTRY_RES_CMD:
dev_dbg(&dev->pdev->dev, "power gate isolation entry response received\n");
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&dev->wait_pg))
wake_up(&dev->wait_pg);
break;
case MEI_PG_ISOLATION_EXIT_REQ_CMD:
dev_dbg(&dev->pdev->dev, "power gate isolation exit request received\n");
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&dev->wait_pg))
wake_up(&dev->wait_pg);
else
/*
* If the driver is not waiting on this then
* this is HW initiated exit from PG.
* Start runtime pm resume sequence to exit from PG.
*/
pm_request_resume(&dev->pdev->dev);
break;
case HOST_CLIENT_PROPERTIES_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
......
......@@ -50,6 +50,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
}
void mei_hbm_idle(struct mei_device *dev);
void mei_hbm_reset(struct mei_device *dev);
int mei_hbm_start_req(struct mei_device *dev);
int mei_hbm_start_wait(struct mei_device *dev);
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
......@@ -57,6 +58,7 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl);
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
bool mei_hbm_version_is_supported(struct mei_device *dev);
int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd);
#endif /* _MEI_HBM_H_ */
......@@ -133,6 +133,8 @@
#define ME_CB_RW 8
/* ME_CSR_HA - ME Control Status Host Access register (read only) */
#define ME_CSR_HA 0xC
/* H_HGC_CSR - PGI register */
#define H_HPG_CSR 0x10
/* register bits of H_CSR (Host Control Status register) */
......@@ -162,6 +164,8 @@ access to ME_CBD */
#define ME_CBWP_HRA 0x00FF0000
/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
#define ME_CBRP_HRA 0x0000FF00
/* ME Power Gate Isolation Capability HRA - host ready only access */
#define ME_PGIC_HRA 0x00000040
/* ME Reset HRA - host read only access to ME_RST */
#define ME_RST_HRA 0x00000010
/* ME Ready HRA - host read only access to ME_RDY */
......@@ -173,4 +177,9 @@ access to ME_CBD */
/* ME Interrupt Enable HRA - host read only access to ME_IE */
#define ME_IE_HRA 0x00000001
/* register bits - H_HPG_CSR */
#define H_HPG_CSR_PGIHEXR 0x00000001
#define H_HPG_CSR_PGI 0x00000002
#endif /* _MEI_HW_MEI_REGS_H_ */
......@@ -109,10 +109,27 @@ static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)
*/
static void mei_me_hw_config(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr = mei_hcsr_read(to_me_hw(dev));
/* Doesn't change in runtime */
dev->hbuf_depth = (hcsr & H_CBD) >> 24;
hw->pg_state = MEI_PG_OFF;
}
/**
* mei_me_pg_state - translate internal pg state
* to the mei power gating state
*
* @hw - me hardware
* returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
*/
static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
return hw->pg_state;
}
/**
* mei_clear_interrupts - clear and stop interrupts
*
......@@ -164,6 +181,9 @@ static void mei_me_hw_reset_release(struct mei_device *dev)
hcsr |= H_IG;
hcsr &= ~H_RST;
mei_hcsr_set(hw, hcsr);
/* complete this write before we set host ready on another CPU */
mmiowb();
}
/**
* mei_me_hw_reset - resets fw via mei csr register.
......@@ -183,8 +203,21 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
else
hcsr &= ~H_IE;
dev->recvd_hw_ready = false;
mei_me_reg_write(hw, H_CSR, hcsr);
/*
* Host reads the H_CSR once to ensure that the
* posted write to H_CSR completes.
*/
hcsr = mei_hcsr_read(hw);
if ((hcsr & H_RST) == 0)
dev_warn(&dev->pdev->dev, "H_RST is not set = 0x%08X", hcsr);
if ((hcsr & H_RDY) == H_RDY)
dev_warn(&dev->pdev->dev, "H_RDY is not cleared 0x%08X", hcsr);
if (intr_enable == false)
mei_me_hw_reset_release(dev);
......@@ -201,6 +234,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
static void mei_me_host_set_ready(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
hw->host_hw_state = mei_hcsr_read(hw);
hw->host_hw_state |= H_IE | H_IG | H_RDY;
mei_hcsr_set(hw, hw->host_hw_state);
}
......@@ -233,10 +267,7 @@ static bool mei_me_hw_is_ready(struct mei_device *dev)
static int mei_me_hw_ready_wait(struct mei_device *dev)
{
int err;
if (mei_me_hw_is_ready(dev))
return 0;
dev->recvd_hw_ready = false;
mutex_unlock(&dev->device_lock);
err = wait_event_interruptible_timeout(dev->wait_hw_ready,
dev->recvd_hw_ready,
......@@ -430,6 +461,144 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
return 0;
}
/**
* mei_me_pg_enter - write pg enter register to mei device.
*
* @dev: the device structure
*/
static void mei_me_pg_enter(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
reg |= H_HPG_CSR_PGI;
mei_me_reg_write(hw, H_HPG_CSR, reg);
}
/**
* mei_me_pg_enter - write pg enter register to mei device.
*
* @dev: the device structure
*/
static void mei_me_pg_exit(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
reg |= H_HPG_CSR_PGIHEXR;
mei_me_reg_write(hw, H_HPG_CSR, reg);
}
/**
* mei_me_pg_set_sync - perform pg entry procedure
*
* @dev: the device structure
*
* returns 0 on success an error code otherwise
*/
int mei_me_pg_set_sync(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
int ret;
dev->pg_event = MEI_PG_EVENT_WAIT;
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
if (ret)
return ret;
mutex_unlock(&dev->device_lock);
wait_event_timeout(dev->wait_pg,
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
mutex_lock(&dev->device_lock);
if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
mei_me_pg_enter(dev);
ret = 0;
} else {
ret = -ETIME;
}
dev->pg_event = MEI_PG_EVENT_IDLE;
hw->pg_state = MEI_PG_ON;
return ret;
}
/**
* mei_me_pg_unset_sync - perform pg exit procedure
*
* @dev: the device structure
*
* returns 0 on success an error code otherwise
*/
int mei_me_pg_unset_sync(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
int ret;
if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
goto reply;
dev->pg_event = MEI_PG_EVENT_WAIT;
mei_me_pg_exit(dev);
mutex_unlock(&dev->device_lock);
wait_event_timeout(dev->wait_pg,
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
mutex_lock(&dev->device_lock);
reply:
if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
else
ret = -ETIME;
dev->pg_event = MEI_PG_EVENT_IDLE;
hw->pg_state = MEI_PG_OFF;
return ret;
}
/**
* mei_me_pg_is_enabled - detect if PG is supported by HW
*
* @dev: the device structure
*
* returns: true is pg supported, false otherwise
*/
static bool mei_me_pg_is_enabled(struct mei_device *dev)
{
struct mei_me_hw *hw = to_me_hw(dev);
u32 reg = mei_me_reg_read(hw, ME_CSR_HA);
if ((reg & ME_PGIC_HRA) == 0)
goto notsupported;
if (dev->version.major_version < HBM_MAJOR_VERSION_PGI)
goto notsupported;
if (dev->version.major_version == HBM_MAJOR_VERSION_PGI &&
dev->version.minor_version < HBM_MINOR_VERSION_PGI)
goto notsupported;
return true;
notsupported:
dev_dbg(&dev->pdev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n",
!!(reg & ME_PGIC_HRA),
dev->version.major_version,
dev->version.minor_version,
HBM_MAJOR_VERSION_PGI,
HBM_MINOR_VERSION_PGI);
return false;
}
/**
* mei_me_irq_quick_handler - The ISR of the MEI device
*
......@@ -491,14 +660,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* check if we need to start the dev */
if (!mei_host_is_ready(dev)) {
if (mei_hw_is_ready(dev)) {
mei_me_hw_reset_release(dev);
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
dev->recvd_hw_ready = true;
wake_up_interruptible(&dev->wait_hw_ready);
} else {
dev_dbg(&dev->pdev->dev, "Reset Completed.\n");
mei_me_hw_reset_release(dev);
dev_dbg(&dev->pdev->dev, "Spurious Interrupt\n");
}
goto end;
}
......@@ -524,9 +692,15 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
rets = mei_irq_write_handler(dev, &complete_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
/*
* During PG handshake only allowed write is the replay to the
* PG exit message, so block calling write function
* if the pg state is not idle
*/
if (dev->pg_event == MEI_PG_EVENT_IDLE) {
rets = mei_irq_write_handler(dev, &complete_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
}
mei_irq_compl_handler(dev, &complete_list);
......@@ -535,8 +709,65 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
/**
* mei_me_fw_status - retrieve fw status from the pci config space
*
* @dev: the device structure
* @fw_status: fw status registers storage
*
* returns 0 on success an error code otherwise
*/
static int mei_me_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
const u32 pci_cfg_reg[] = {PCI_CFG_HFS_1, PCI_CFG_HFS_2};
int i;
if (!fw_status)
return -EINVAL;
switch (dev->pdev->device) {
case MEI_DEV_ID_IBXPK_1:
case MEI_DEV_ID_IBXPK_2:
case MEI_DEV_ID_CPT_1:
case MEI_DEV_ID_PBG_1:
case MEI_DEV_ID_PPT_1:
case MEI_DEV_ID_PPT_2:
case MEI_DEV_ID_PPT_3:
case MEI_DEV_ID_LPT_H:
case MEI_DEV_ID_LPT_W:
case MEI_DEV_ID_LPT_LP:
case MEI_DEV_ID_LPT_HR:
case MEI_DEV_ID_WPT_LP:
fw_status->count = 2;
break;
case MEI_DEV_ID_ICH10_1:
case MEI_DEV_ID_ICH10_2:
case MEI_DEV_ID_ICH10_3:
case MEI_DEV_ID_ICH10_4:
fw_status->count = 1;
break;
default:
fw_status->count = 0;
break;
}
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
pci_cfg_reg[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
static const struct mei_hw_ops mei_me_hw_ops = {
.pg_state = mei_me_pg_state,
.fw_status = mei_me_fw_status,
.host_is_ready = mei_me_host_is_ready,
.hw_is_ready = mei_me_hw_is_ready,
......@@ -544,6 +775,8 @@ static const struct mei_hw_ops mei_me_hw_ops = {
.hw_config = mei_me_hw_config,
.hw_start = mei_me_hw_start,
.pg_is_enabled = mei_me_pg_is_enabled,
.intr_clear = mei_me_intr_clear,
.intr_enable = mei_me_intr_enable,
.intr_disable = mei_me_intr_disable,
......@@ -559,14 +792,81 @@ static const struct mei_hw_ops mei_me_hw_ops = {
.read = mei_me_read_slots
};
static bool mei_me_fw_type_nm(struct pci_dev *pdev)
{
u32 reg;
pci_read_config_dword(pdev, PCI_CFG_HFS_2, &reg);
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
return (reg & 0x600) == 0x200;
}
#define MEI_CFG_FW_NM \
.quirk_probe = mei_me_fw_type_nm
static bool mei_me_fw_type_sps(struct pci_dev *pdev)
{
u32 reg;
/* Read ME FW Status check for SPS Firmware */
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
/* if bits [19:16] = 15, running SPS Firmware */
return (reg & 0xf0000) == 0xf0000;
}
#define MEI_CFG_FW_SPS \
.quirk_probe = mei_me_fw_type_sps
#define MEI_CFG_LEGACY_HFS \
.fw_status.count = 0
#define MEI_CFG_ICH_HFS \
.fw_status.count = 1, \
.fw_status.status[0] = PCI_CFG_HFS_1
#define MEI_CFG_PCH_HFS \
.fw_status.count = 2, \
.fw_status.status[0] = PCI_CFG_HFS_1, \
.fw_status.status[1] = PCI_CFG_HFS_2
/* ICH Legacy devices */
const struct mei_cfg mei_me_legacy_cfg = {
MEI_CFG_LEGACY_HFS,
};
/* ICH devices */
const struct mei_cfg mei_me_ich_cfg = {
MEI_CFG_ICH_HFS,
};
/* PCH devices */
const struct mei_cfg mei_me_pch_cfg = {
MEI_CFG_PCH_HFS,
};
/* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */
const struct mei_cfg mei_me_pch_cpt_pbg_cfg = {
MEI_CFG_PCH_HFS,
MEI_CFG_FW_NM,
};
/* PCH Lynx Point with quirk for SPS Firmware exclusion */
const struct mei_cfg mei_me_lpt_cfg = {
MEI_CFG_PCH_HFS,
MEI_CFG_FW_SPS,
};
/**
* mei_me_dev_init - allocates and initializes the mei device structure
*
* @pdev: The pci device structure
* @cfg: per device generation config
*
* returns The mei_device_device pointer on success, NULL on failure.
*/
struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg)
{
struct mei_device *dev;
......@@ -575,7 +875,7 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
if (!dev)
return NULL;
mei_device_init(dev);
mei_device_init(dev, cfg);
dev->ops = &mei_me_hw_ops;
......
......@@ -24,6 +24,8 @@
#include "mei_dev.h"
#include "client.h"
#define MEI_ME_RPM_TIMEOUT 500 /* ms */
struct mei_me_hw {
void __iomem *mem_addr;
/*
......@@ -31,11 +33,22 @@ struct mei_me_hw {
*/
u32 host_hw_state;
u32 me_hw_state;
enum mei_pg_state pg_state;
};
#define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw)
struct mei_device *mei_me_dev_init(struct pci_dev *pdev);
extern const struct mei_cfg mei_me_legacy_cfg;
extern const struct mei_cfg mei_me_ich_cfg;
extern const struct mei_cfg mei_me_pch_cfg;
extern const struct mei_cfg mei_me_pch_cpt_pbg_cfg;
extern const struct mei_cfg mei_me_lpt_cfg;
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg);
int mei_me_pg_set_sync(struct mei_device *dev);
int mei_me_pg_unset_sync(struct mei_device *dev);
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
......
......@@ -89,7 +89,7 @@ enum {
# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000
# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000
# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000
#define PCI_CFG_TXE_FW_STS1 0x48
#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */
......
......@@ -158,7 +158,7 @@ static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req)
dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n",
hw->aliveness, req);
if (do_req) {
hw->recvd_aliveness = false;
dev->pg_event = MEI_PG_EVENT_WAIT;
mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req);
}
return do_req;
......@@ -213,6 +213,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
do {
hw->aliveness = mei_txe_aliveness_get(dev);
if (hw->aliveness == expected) {
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_dbg(&dev->pdev->dev,
"aliveness settled after %d msecs\n", t);
return t;
......@@ -223,6 +224,7 @@ static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected)
t += MSEC_PER_SEC / 5;
} while (t < SEC_ALIVENESS_WAIT_TIMEOUT);
dev->pg_event = MEI_PG_EVENT_IDLE;
dev_err(&dev->pdev->dev, "aliveness timed out\n");
return -ETIME;
}
......@@ -249,19 +251,22 @@ static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected)
return 0;
mutex_unlock(&dev->device_lock);
err = wait_event_timeout(hw->wait_aliveness,
hw->recvd_aliveness, timeout);
err = wait_event_timeout(hw->wait_aliveness_resp,
dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout);
mutex_lock(&dev->device_lock);
hw->aliveness = mei_txe_aliveness_get(dev);
ret = hw->aliveness == expected ? 0 : -ETIME;
if (ret)
dev_err(&dev->pdev->dev, "aliveness timed out");
dev_warn(&dev->pdev->dev, "aliveness timed out = %ld aliveness = %d event = %d\n",
err, hw->aliveness, dev->pg_event);
else
dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n",
jiffies_to_msecs(timeout - err));
hw->recvd_aliveness = false;
dev_dbg(&dev->pdev->dev, "aliveness settled after = %d msec aliveness = %d event = %d\n",
jiffies_to_msecs(timeout - err),
hw->aliveness, dev->pg_event);
dev->pg_event = MEI_PG_EVENT_IDLE;
return ret;
}
......@@ -279,6 +284,32 @@ int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req)
return 0;
}
/**
* mei_txe_pg_is_enabled - detect if PG is supported by HW
*
* @dev: the device structure
*
* returns: true is pg supported, false otherwise
*/
static bool mei_txe_pg_is_enabled(struct mei_device *dev)
{
return true;
}
/**
* mei_txe_pg_state - translate aliveness register value
* to the mei power gating state
*
* @dev: the device structure
*
* returns: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
*/
static inline enum mei_pg_state mei_txe_pg_state(struct mei_device *dev)
{
struct mei_txe_hw *hw = to_txe_hw(dev);
return hw->aliveness ? MEI_PG_OFF : MEI_PG_ON;
}
/**
* mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt
*
......@@ -589,7 +620,10 @@ static int mei_txe_write(struct mei_device *dev,
mei_txe_input_ready_interrupt_enable(dev);
if (!mei_txe_is_input_ready(dev)) {
dev_err(&dev->pdev->dev, "Input is not ready");
struct mei_fw_status fw_status;
mei_fw_status(dev, &fw_status);
dev_err(&dev->pdev->dev, "Input is not ready " FW_STS_FMT "\n",
FW_STS_PRM(fw_status));
return -EAGAIN;
}
......@@ -960,9 +994,9 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
/* Clear the interrupt cause */
dev_dbg(&dev->pdev->dev,
"Aliveness Interrupt: Status: %d\n", hw->aliveness);
hw->recvd_aliveness = true;
if (waitqueue_active(&hw->wait_aliveness))
wake_up(&hw->wait_aliveness);
dev->pg_event = MEI_PG_EVENT_RECEIVED;
if (waitqueue_active(&hw->wait_aliveness_resp))
wake_up(&hw->wait_aliveness_resp);
}
......@@ -1008,15 +1042,51 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
/**
* mei_txe_fw_status - retrieve fw status from the pci config space
*
* @dev: the device structure
* @fw_status: fw status registers storage
*
* returns: 0 on success an error code otherwise
*/
static int mei_txe_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
const u32 pci_cfg_reg[] = {PCI_CFG_TXE_FW_STS0, PCI_CFG_TXE_FW_STS1};
int i;
if (!fw_status)
return -EINVAL;
fw_status->count = 2;
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
pci_cfg_reg[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
static const struct mei_hw_ops mei_txe_hw_ops = {
.fw_status = mei_txe_fw_status,
.host_is_ready = mei_txe_host_is_ready,
.pg_state = mei_txe_pg_state,
.hw_is_ready = mei_txe_hw_is_ready,
.hw_reset = mei_txe_hw_reset,
.hw_config = mei_txe_hw_config,
.hw_start = mei_txe_hw_start,
.pg_is_enabled = mei_txe_pg_is_enabled,
.intr_clear = mei_txe_intr_clear,
.intr_enable = mei_txe_intr_enable,
.intr_disable = mei_txe_intr_disable,
......@@ -1034,14 +1104,27 @@ static const struct mei_hw_ops mei_txe_hw_ops = {
};
#define MEI_CFG_TXE_FW_STS \
.fw_status.count = 2, \
.fw_status.status[0] = PCI_CFG_TXE_FW_STS0, \
.fw_status.status[1] = PCI_CFG_TXE_FW_STS1
const struct mei_cfg mei_txe_cfg = {
MEI_CFG_TXE_FW_STS,
};
/**
* mei_txe_dev_init - allocates and initializes txe hardware specific structure
*
* @pdev - pci device
* @cfg - per device generation config
*
* returns struct mei_device * on success or NULL;
*
*/
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg)
{
struct mei_device *dev;
struct mei_txe_hw *hw;
......@@ -1051,11 +1134,11 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
if (!dev)
return NULL;
mei_device_init(dev);
mei_device_init(dev, cfg);
hw = to_txe_hw(dev);
init_waitqueue_head(&hw->wait_aliveness);
init_waitqueue_head(&hw->wait_aliveness_resp);
dev->ops = &mei_txe_hw_ops;
......
......@@ -22,6 +22,8 @@
#include "hw.h"
#include "hw-txe-regs.h"
#define MEI_TXI_RPM_TIMEOUT 500 /* ms */
/* Flatten Hierarchy interrupt cause */
#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */
#define TXE_INTR_READINESS HISR_INT_0_STS
......@@ -35,12 +37,11 @@
/**
* struct mei_txe_hw - txe hardware specifics
*
* @mem_addr: SeC and BRIDGE bars
* @aliveness: aliveness (power gating) state of the hardware
* @readiness: readiness state of the hardware
* @wait_aliveness: aliveness wait queue
* @recvd_aliveness: aliveness interrupt was recived
* @intr_cause: translated interrupt cause
* @mem_addr: SeC and BRIDGE bars
* @aliveness: aliveness (power gating) state of the hardware
* @readiness: readiness state of the hardware
* @wait_aliveness_resp: aliveness wait queue
* @intr_cause: translated interrupt cause
*/
struct mei_txe_hw {
void __iomem *mem_addr[NUM_OF_MEM_BARS];
......@@ -48,8 +49,7 @@ struct mei_txe_hw {
u32 readiness;
u32 slots;
wait_queue_head_t wait_aliveness;
bool recvd_aliveness;
wait_queue_head_t wait_aliveness_resp;
unsigned long intr_cause;
};
......@@ -61,7 +61,10 @@ static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw)
return container_of((void *)hw, struct mei_device, hw);
}
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev);
extern const struct mei_cfg mei_txe_cfg;
struct mei_device *mei_txe_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg);
irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id);
irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id);
......
......@@ -31,14 +31,21 @@
#define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */
#define MEI_IAMTHIF_READ_TIMER 10 /* HPS */
#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */
#define MEI_HBM_TIMEOUT 1 /* 1 second */
/*
* MEI Version
*/
#define HBM_MINOR_VERSION 0
#define HBM_MINOR_VERSION 1
#define HBM_MAJOR_VERSION 1
/*
* MEI version with PGI support
*/
#define HBM_MINOR_VERSION_PGI 1
#define HBM_MAJOR_VERSION_PGI 1
/* Host bus message command opcode */
#define MEI_HBM_CMD_OP_MSK 0x7f
/* Host bus message command RESPONSE */
......@@ -69,6 +76,11 @@
#define MEI_FLOW_CONTROL_CMD 0x08
#define MEI_PG_ISOLATION_ENTRY_REQ_CMD 0x0a
#define MEI_PG_ISOLATION_ENTRY_RES_CMD 0x8a
#define MEI_PG_ISOLATION_EXIT_REQ_CMD 0x0b
#define MEI_PG_ISOLATION_EXIT_RES_CMD 0x8b
/*
* MEI Stop Reason
* used by hbm_host_stop_request.reason
......@@ -207,6 +219,17 @@ struct hbm_props_response {
struct mei_client_properties client_properties;
} __packed;
/**
* struct hbm_power_gate - power gate request/response
*
* @hbm_cmd - bus message command header
* @reserved[3]
*/
struct hbm_power_gate {
u8 hbm_cmd;
u8 reserved[3];
} __packed;
/**
* struct hbm_client_connect_request - connect/disconnect request
*
......
......@@ -74,9 +74,13 @@ int mei_reset(struct mei_device *dev)
if (state != MEI_DEV_INITIALIZING &&
state != MEI_DEV_DISABLED &&
state != MEI_DEV_POWER_DOWN &&
state != MEI_DEV_POWER_UP)
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
mei_dev_state_str(state));
state != MEI_DEV_POWER_UP) {
struct mei_fw_status fw_status;
mei_fw_status(dev, &fw_status);
dev_warn(&dev->pdev->dev,
"unexpected reset: dev_state = %s " FW_STS_FMT "\n",
mei_dev_state_str(state), FW_STS_PRM(fw_status));
}
/* we're already in reset, cancel the init timer
* if the reset was called due the hbm protocol error
......@@ -118,8 +122,8 @@ int mei_reset(struct mei_device *dev)
mei_amthif_reset_params(dev);
}
mei_hbm_reset(dev);
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
dev->wd_pending = false;
......@@ -303,15 +307,58 @@ void mei_stop(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_stop);
/**
* mei_write_is_idle - check if the write queues are idle
*
* @dev: the device structure
*
* returns true of there is no pending write
*/
bool mei_write_is_idle(struct mei_device *dev)
{
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
list_empty(&dev->ctrl_wr_list.list) &&
list_empty(&dev->write_list.list));
dev_dbg(&dev->pdev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d\n",
idle,
mei_dev_state_str(dev->dev_state),
list_empty(&dev->ctrl_wr_list.list),
list_empty(&dev->write_list.list));
void mei_device_init(struct mei_device *dev)
return idle;
}
EXPORT_SYMBOL_GPL(mei_write_is_idle);
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status)
{
int i;
const struct mei_fw_status *fw_src = &dev->cfg->fw_status;
if (!fw_status)
return -EINVAL;
fw_status->count = fw_src->count;
for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
fw_src->status[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(mei_fw_status);
void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg)
{
/* setup our list array */
INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_pg);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
......@@ -340,6 +387,9 @@ void mei_device_init(struct mei_device *dev)
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
dev->pg_event = MEI_PG_EVENT_IDLE;
dev->cfg = cfg;
}
EXPORT_SYMBOL_GPL(mei_device_init);
......@@ -467,7 +467,6 @@ static int mei_ioctl_connect_client(struct file *file,
}
cl->me_client_id = dev->me_clients[i].client_id;
cl->state = MEI_FILE_CONNECTING;
dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
cl->me_client_id);
......
......@@ -153,6 +153,20 @@ struct mei_msg_data {
unsigned char *data;
};
/* Maximum number of processed FW status registers */
#define MEI_FW_STATUS_MAX 2
/*
* struct mei_fw_status - storage of FW status data
*
* @count - number of actually available elements in array
* @status - FW status registers
*/
struct mei_fw_status {
int count;
u32 status[MEI_FW_STATUS_MAX];
};
/**
* struct mei_me_client - representation of me (fw) client
*
......@@ -213,6 +227,7 @@ struct mei_cl {
/** struct mei_hw_ops
*
* @fw_status - read FW status from PCI config space
* @host_is_ready - query for host readiness
* @hw_is_ready - query if hw is ready
......@@ -220,6 +235,9 @@ struct mei_cl {
* @hw_start - start hw after reset
* @hw_config - configure hw
* @pg_state - power gating state of the device
* @pg_is_enabled - is power gating enabled
* @intr_clear - clear pending interrupts
* @intr_enable - enable interrupts
* @intr_disable - disable interrupts
......@@ -237,6 +255,8 @@ struct mei_cl {
*/
struct mei_hw_ops {
int (*fw_status)(struct mei_device *dev,
struct mei_fw_status *fw_status);
bool (*host_is_ready)(struct mei_device *dev);
bool (*hw_is_ready)(struct mei_device *dev);
......@@ -244,6 +264,9 @@ struct mei_hw_ops {
int (*hw_start)(struct mei_device *dev);
void (*hw_config)(struct mei_device *dev);
enum mei_pg_state (*pg_state)(struct mei_device *dev);
bool (*pg_is_enabled)(struct mei_device *dev);
void (*intr_clear)(struct mei_device *dev);
void (*intr_enable)(struct mei_device *dev);
void (*intr_disable)(struct mei_device *dev);
......@@ -331,16 +354,61 @@ struct mei_cl_device {
void *priv_data;
};
/**
* enum mei_pg_event - power gating transition events
*
* @MEI_PG_EVENT_IDLE: the driver is not in power gating transition
* @MEI_PG_EVENT_WAIT: the driver is waiting for a pg event to complete
* @MEI_PG_EVENT_RECEIVED: the driver received pg event
*/
enum mei_pg_event {
MEI_PG_EVENT_IDLE,
MEI_PG_EVENT_WAIT,
MEI_PG_EVENT_RECEIVED,
};
/**
* enum mei_pg_state - device internal power gating state
*
* @MEI_PG_OFF: device is not power gated - it is active
* @MEI_PG_ON: device is power gated - it is in lower power state
*/
enum mei_pg_state {
MEI_PG_OFF = 0,
MEI_PG_ON = 1,
};
/*
* mei_cfg
*
* @fw_status - FW status
* @quirk_probe - device exclusion quirk
*/
struct mei_cfg {
const struct mei_fw_status fw_status;
bool (*quirk_probe)(struct pci_dev *pdev);
};
#define MEI_PCI_DEVICE(dev, cfg) \
.vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \
.driver_data = (kernel_ulong_t)&(cfg)
/**
* struct mei_device - MEI private device struct
* @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol
* @pg_event - power gating event
* @mem_addr - mem mapped base register address
* @hbuf_depth - depth of hardware host/write buffer is slots
* @hbuf_is_ready - query if the host host/write buffer is ready
* @wr_msg - the buffer for hbm control messages
* @cfg - per device generation config and ops
*/
struct mei_device {
struct pci_dev *pdev; /* pointer to pci device struct */
......@@ -371,6 +439,7 @@ struct mei_device {
* waiting queue for receive message from FW
*/
wait_queue_head_t wait_hw_ready;
wait_queue_head_t wait_pg;
wait_queue_head_t wait_recvd_msg;
wait_queue_head_t wait_stop_wd;
......@@ -382,6 +451,14 @@ struct mei_device {
enum mei_hbm_state hbm_state;
u16 init_clients_timer;
/*
* Power Gating support
*/
enum mei_pg_event pg_event;
#ifdef CONFIG_PM_RUNTIME
struct dev_pm_domain pg_domain;
#endif /* CONFIG_PM_RUNTIME */
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
u32 rd_msg_hdr;
......@@ -442,6 +519,7 @@ struct mei_device {
const struct mei_hw_ops *ops;
const struct mei_cfg *cfg;
char hw[0] __aligned(sizeof(void *));
};
......@@ -474,7 +552,7 @@ static inline u32 mei_slots2data(int slots)
/*
* mei init function prototypes
*/
void mei_device_init(struct mei_device *dev);
void mei_device_init(struct mei_device *dev, const struct mei_cfg *cfg);
int mei_reset(struct mei_device *dev);
int mei_start(struct mei_device *dev);
int mei_restart(struct mei_device *dev);
......@@ -553,10 +631,22 @@ void mei_watchdog_unregister(struct mei_device *dev);
* Register Access Function
*/
static inline void mei_hw_config(struct mei_device *dev)
{
dev->ops->hw_config(dev);
}
static inline enum mei_pg_state mei_pg_state(struct mei_device *dev)
{
return dev->ops->pg_state(dev);
}
static inline bool mei_pg_is_enabled(struct mei_device *dev)
{
return dev->ops->pg_is_enabled(dev);
}
static inline int mei_hw_reset(struct mei_device *dev, bool enable)
{
return dev->ops->hw_reset(dev, enable);
......@@ -629,8 +719,17 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
return dev->ops->rdbuf_full_slots(dev);
}
int mei_fw_status(struct mei_device *dev, struct mei_fw_status *fw_status);
#define FW_STS_FMT "%08X %08X"
#define FW_STS_PRM(fw_status) \
(fw_status).count > 0 ? (fw_status).status[0] : 0xDEADBEEF, \
(fw_status).count > 1 ? (fw_status).status[1] : 0xDEADBEEF
bool mei_hbuf_acquire(struct mei_device *dev);
bool mei_write_is_idle(struct mei_device *dev);
#if IS_ENABLED(CONFIG_DEBUG_FS)
int mei_dbgfs_register(struct mei_device *dev, const char *name);
void mei_dbgfs_deregister(struct mei_device *dev);
......
......@@ -33,6 +33,8 @@
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h>
#include "mei_dev.h"
......@@ -42,42 +44,44 @@
/* mei_pci_tbl - PCI Device ID Table */
static const struct pci_device_id mei_me_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_H)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_HR)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_WPT_LP)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_lpt_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_lpt_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)},
/* required last entry */
{0, }
......@@ -85,44 +89,33 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
#ifdef CONFIG_PM_RUNTIME
static inline void mei_me_set_pm_domain(struct mei_device *dev);
static inline void mei_me_unset_pm_domain(struct mei_device *dev);
#else
static inline void mei_me_set_pm_domain(struct mei_device *dev) {}
static inline void mei_me_unset_pm_domain(struct mei_device *dev) {}
#endif /* CONFIG_PM_RUNTIME */
/**
* mei_quirk_probe - probe for devices that doesn't valid ME interface
*
* @pdev: PCI device structure
* @ent: entry into pci_device_table
* @cfg: per generation config
*
* returns true if ME Interface is valid, false otherwise
*/
static bool mei_me_quirk_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
const struct mei_cfg *cfg)
{
u32 reg;
/* Cougar Point || Patsburg */
if (ent->device == MEI_DEV_ID_CPT_1 ||
ent->device == MEI_DEV_ID_PBG_1) {
pci_read_config_dword(pdev, PCI_CFG_HFS_2, &reg);
/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
if ((reg & 0x600) == 0x200)
goto no_mei;
}
/* Lynx Point */
if (ent->device == MEI_DEV_ID_LPT_H ||
ent->device == MEI_DEV_ID_LPT_W ||
ent->device == MEI_DEV_ID_LPT_HR) {
/* Read ME FW Status check for SPS Firmware */
pci_read_config_dword(pdev, PCI_CFG_HFS_1, &reg);
/* if bits [19:16] = 15, running SPS Firmware */
if ((reg & 0xf0000) == 0xf0000)
goto no_mei;
if (cfg->quirk_probe && cfg->quirk_probe(pdev)) {
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
return false;
}
return true;
no_mei:
dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
return false;
}
/**
* mei_probe - Device Initialization Routine
*
......@@ -133,15 +126,14 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev,
*/
static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
struct mei_device *dev;
struct mei_me_hw *hw;
int err;
if (!mei_me_quirk_probe(pdev, ent)) {
err = -ENODEV;
goto end;
}
if (!mei_me_quirk_probe(pdev, cfg))
return -ENODEV;
/* enable pci dev */
err = pci_enable_device(pdev);
......@@ -173,7 +165,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* allocates and initializes the mei dev structure */
dev = mei_me_dev_init(pdev);
dev = mei_me_dev_init(pdev, cfg);
if (!dev) {
err = -ENOMEM;
goto release_regions;
......@@ -212,6 +204,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_irq;
}
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
err = mei_register(dev);
if (err)
goto release_irq;
......@@ -220,6 +215,17 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
schedule_delayed_work(&dev->timer_work, HZ);
/*
* For not wake-able HW runtime pm framework
* can't be used on pci device level.
* Use domain runtime pm callbacks instead.
*/
if (!pci_dev_run_wake(pdev))
mei_me_set_pm_domain(dev);
if (mei_pg_is_enabled(dev))
pm_runtime_put_noidle(&pdev->dev);
dev_dbg(&pdev->dev, "initialization successful.\n");
return 0;
......@@ -259,12 +265,18 @@ static void mei_me_remove(struct pci_dev *pdev)
if (!dev)
return;
if (mei_pg_is_enabled(dev))
pm_runtime_get_noresume(&pdev->dev);
hw = to_me_hw(dev);
dev_dbg(&pdev->dev, "stop\n");
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_me_unset_pm_domain(dev);
/* disable interrupts */
mei_disable_interrupts(dev);
......@@ -343,12 +355,120 @@ static int mei_me_pci_resume(struct device *device)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
static int mei_me_pm_runtime_idle(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct mei_device *dev;
dev_dbg(&pdev->dev, "rpm: me: runtime_idle\n");
dev = pci_get_drvdata(pdev);
if (!dev)
return -ENODEV;
if (mei_write_is_idle(dev))
pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2);
return -EBUSY;
}
static int mei_me_pm_runtime_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct mei_device *dev;
int ret;
dev_dbg(&pdev->dev, "rpm: me: runtime suspend\n");
dev = pci_get_drvdata(pdev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
if (mei_write_is_idle(dev))
ret = mei_me_pg_set_sync(dev);
else
ret = -EAGAIN;
mutex_unlock(&dev->device_lock);
dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret);
return ret;
}
static int mei_me_pm_runtime_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct mei_device *dev;
int ret;
dev_dbg(&pdev->dev, "rpm: me: runtime resume\n");
dev = pci_get_drvdata(pdev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
ret = mei_me_pg_unset_sync(dev);
mutex_unlock(&dev->device_lock);
dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret);
return ret;
}
/**
* mei_me_set_pm_domain - fill and set pm domian stucture for device
*
* @dev: mei_device
*/
static inline void mei_me_set_pm_domain(struct mei_device *dev)
{
struct pci_dev *pdev = dev->pdev;
if (pdev->dev.bus && pdev->dev.bus->pm) {
dev->pg_domain.ops = *pdev->dev.bus->pm;
dev->pg_domain.ops.runtime_suspend = mei_me_pm_runtime_suspend;
dev->pg_domain.ops.runtime_resume = mei_me_pm_runtime_resume;
dev->pg_domain.ops.runtime_idle = mei_me_pm_runtime_idle;
pdev->dev.pm_domain = &dev->pg_domain;
}
}
/**
* mei_me_unset_pm_domain - clean pm domian stucture for device
*
* @dev: mei_device
*/
static inline void mei_me_unset_pm_domain(struct mei_device *dev)
{
/* stop using pm callbacks if any */
dev->pdev->dev.pm_domain = NULL;
}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM
static const struct dev_pm_ops mei_me_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mei_me_pci_suspend,
mei_me_pci_resume)
SET_RUNTIME_PM_OPS(
mei_me_pm_runtime_suspend,
mei_me_pm_runtime_resume,
mei_me_pm_runtime_idle)
};
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
#else
#define MEI_ME_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
/*
* PCI driver structure
*/
......
......@@ -27,6 +27,7 @@
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h>
......@@ -35,11 +36,18 @@
#include "hw-txe.h"
static const struct pci_device_id mei_txe_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */
{MEI_PCI_DEVICE(0x0F18, mei_txe_cfg)}, /* Baytrail */
{0, }
};
MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl);
#ifdef CONFIG_PM_RUNTIME
static inline void mei_txe_set_pm_domain(struct mei_device *dev);
static inline void mei_txe_unset_pm_domain(struct mei_device *dev);
#else
static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
#endif /* CONFIG_PM_RUNTIME */
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
{
......@@ -61,6 +69,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
*/
static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data);
struct mei_device *dev;
struct mei_txe_hw *hw;
int err;
......@@ -91,7 +100,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
/* allocates and initializes the mei dev structure */
dev = mei_txe_dev_init(pdev);
dev = mei_txe_dev_init(pdev, cfg);
if (!dev) {
err = -ENOMEM;
goto release_regions;
......@@ -137,12 +146,25 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto release_irq;
}
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
err = mei_register(dev);
if (err)
goto release_irq;
pci_set_drvdata(pdev, dev);
/*
* For not wake-able HW runtime pm framework
* can't be used on pci device level.
* Use domain runtime pm callbacks instead.
*/
if (!pci_dev_run_wake(pdev))
mei_txe_set_pm_domain(dev);
pm_runtime_put_noidle(&pdev->dev);
return 0;
release_irq:
......@@ -187,10 +209,15 @@ static void mei_txe_remove(struct pci_dev *pdev)
return;
}
pm_runtime_get_noresume(&pdev->dev);
hw = to_txe_hw(dev);
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_txe_unset_pm_domain(dev);
/* disable interrupts */
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
......@@ -265,15 +292,131 @@ static int mei_txe_pci_resume(struct device *device)
return err;
}
#endif /* CONFIG_PM_SLEEP */
#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_pci_resume);
/**
* mei_txe_unset_pm_domain - clean pm domian stucture for device
*
* @dev: mei_device
*/
static inline void mei_txe_unset_pm_domain(struct mei_device *dev)
{
/* stop using pm callbacks if any */
dev->pdev->dev.pm_domain = NULL;
}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM
static const struct dev_pm_ops mei_txe_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mei_txe_pci_suspend,
mei_txe_pci_resume)
SET_RUNTIME_PM_OPS(
mei_txe_pm_runtime_suspend,
mei_txe_pm_runtime_resume,
mei_txe_pm_runtime_idle)
};
#define MEI_TXE_PM_OPS (&mei_txe_pm_ops)
#else
#define MEI_TXE_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM */
/*
* PCI driver structure
*/
......
......@@ -84,8 +84,6 @@ int mei_wd_host_init(struct mei_device *dev)
return ret;
}
cl->state = MEI_FILE_CONNECTING;
ret = mei_cl_connect(cl, NULL);
if (ret) {
......
......@@ -266,11 +266,12 @@ config REGULATOR_LP8788
This driver supports LP8788 voltage regulator chip.
config REGULATOR_MAX14577
tristate "Maxim 14577 regulator"
tristate "Maxim 14577/77836 regulator"
depends on MFD_MAX14577
help
This driver controls a Maxim 14577 regulator via I2C bus.
The regulators include safeout LDO and current regulator 'CHARGER'.
This driver controls a Maxim MAX14577/77836 regulator via I2C bus.
The MAX14577 regulators include safeout LDO and charger current
regulator. The MAX77836 has two additional LDOs.
config REGULATOR_MAX1586
tristate "Maxim 1586/1587 voltage regulator"
......
/*
* max14577.c - Regulator driver for the Maxim 14577
* max14577.c - Regulator driver for the Maxim 14577/77836
*
* Copyright (C) 2013,2014 Samsung Electronics
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
......@@ -22,6 +22,42 @@
#include <linux/mfd/max14577-private.h>
#include <linux/regulator/of_regulator.h>
/*
* Valid limits of current for max14577 and max77836 chargers.
* They must correspond to MBCICHWRCL and MBCICHWRCH fields in CHGCTRL4
* register for given chipset.
*/
struct maxim_charger_current {
/* Minimal current, set in CHGCTRL4/MBCICHWRCL, uA */
unsigned int min;
/*
* Minimal current when high setting is active,
* set in CHGCTRL4/MBCICHWRCH, uA
*/
unsigned int high_start;
/* Value of one step in high setting, uA */
unsigned int high_step;
/* Maximum current of high setting, uA */
unsigned int max;
};
/* Table of valid charger currents for different Maxim chipsets */
static const struct maxim_charger_current maxim_charger_currents[] = {
[MAXIM_DEVICE_TYPE_UNKNOWN] = { 0, 0, 0, 0 },
[MAXIM_DEVICE_TYPE_MAX14577] = {
.min = MAX14577_REGULATOR_CURRENT_LIMIT_MIN,
.high_start = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START,
.high_step = MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
.max = MAX14577_REGULATOR_CURRENT_LIMIT_MAX,
},
[MAXIM_DEVICE_TYPE_MAX77836] = {
.min = MAX77836_REGULATOR_CURRENT_LIMIT_MIN,
.high_start = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START,
.high_step = MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP,
.max = MAX77836_REGULATOR_CURRENT_LIMIT_MAX,
},
};
static int max14577_reg_is_enabled(struct regulator_dev *rdev)
{
int rid = rdev_get_id(rdev);
......@@ -47,6 +83,9 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
{
u8 reg_data;
struct regmap *rmap = rdev->regmap;
struct max14577 *max14577 = rdev_get_drvdata(rdev);
const struct maxim_charger_current *limits =
&maxim_charger_currents[max14577->dev_type];
if (rdev_get_id(rdev) != MAX14577_CHARGER)
return -EINVAL;
......@@ -54,12 +93,11 @@ static int max14577_reg_get_current_limit(struct regulator_dev *rdev)
max14577_read_reg(rmap, MAX14577_CHG_REG_CHG_CTRL4, &reg_data);
if ((reg_data & CHGCTRL4_MBCICHWRCL_MASK) == 0)
return MAX14577_REGULATOR_CURRENT_LIMIT_MIN;
return limits->min;
reg_data = ((reg_data & CHGCTRL4_MBCICHWRCH_MASK) >>
CHGCTRL4_MBCICHWRCH_SHIFT);
return MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
reg_data * MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP;
return limits->high_start + reg_data * limits->high_step;
}
static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
......@@ -67,33 +105,39 @@ static int max14577_reg_set_current_limit(struct regulator_dev *rdev,
{
int i, current_bits = 0xf;
u8 reg_data;
struct max14577 *max14577 = rdev_get_drvdata(rdev);
const struct maxim_charger_current *limits =
&maxim_charger_currents[max14577->dev_type];
if (rdev_get_id(rdev) != MAX14577_CHARGER)
return -EINVAL;
if (min_uA > MAX14577_REGULATOR_CURRENT_LIMIT_MAX ||
max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_MIN)
if (min_uA > limits->max || max_uA < limits->min)
return -EINVAL;
if (max_uA < MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START) {
/* Less than 200 mA, so set 90mA (turn only Low Bit off) */
if (max_uA < limits->high_start) {
/*
* Less than high_start,
* so set the minimal current (turn only Low Bit off)
*/
u8 reg_data = 0x0 << CHGCTRL4_MBCICHWRCL_SHIFT;
return max14577_update_reg(rdev->regmap,
MAX14577_CHG_REG_CHG_CTRL4,
CHGCTRL4_MBCICHWRCL_MASK, reg_data);
}
/* max_uA is in range: <LIMIT_HIGH_START, inifinite>, so search for
* valid current starting from LIMIT_MAX. */
for (i = MAX14577_REGULATOR_CURRENT_LIMIT_MAX;
i >= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START;
i -= MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP) {
/*
* max_uA is in range: <high_start, inifinite>, so search for
* valid current starting from maximum current.
*/
for (i = limits->max; i >= limits->high_start; i -= limits->high_step) {
if (i <= max_uA)
break;
current_bits--;
}
BUG_ON(current_bits < 0); /* Cannot happen */
/* Turn Low Bit on (use range 200mA-950 mA) */
/* Turn Low Bit on (use range high_start-max)... */
reg_data = 0x1 << CHGCTRL4_MBCICHWRCL_SHIFT;
/* and set proper High Bits */
reg_data |= current_bits << CHGCTRL4_MBCICHWRCH_SHIFT;
......@@ -118,7 +162,7 @@ static struct regulator_ops max14577_charger_ops = {
.set_current_limit = max14577_reg_set_current_limit,
};
static const struct regulator_desc supported_regulators[] = {
static const struct regulator_desc max14577_supported_regulators[] = {
[MAX14577_SAFEOUT] = {
.name = "SAFEOUT",
.id = MAX14577_SAFEOUT,
......@@ -141,16 +185,88 @@ static const struct regulator_desc supported_regulators[] = {
},
};
static struct regulator_ops max77836_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
/* TODO: add .set_suspend_mode */
};
static const struct regulator_desc max77836_supported_regulators[] = {
[MAX14577_SAFEOUT] = {
.name = "SAFEOUT",
.id = MAX14577_SAFEOUT,
.ops = &max14577_safeout_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = 1,
.min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
.enable_reg = MAX14577_REG_CONTROL2,
.enable_mask = CTRL2_SFOUTORD_MASK,
},
[MAX14577_CHARGER] = {
.name = "CHARGER",
.id = MAX14577_CHARGER,
.ops = &max14577_charger_ops,
.type = REGULATOR_CURRENT,
.owner = THIS_MODULE,
.enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
.enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
},
[MAX77836_LDO1] = {
.name = "LDO1",
.id = MAX77836_LDO1,
.ops = &max77836_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO1,
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1,
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
},
[MAX77836_LDO2] = {
.name = "LDO2",
.id = MAX77836_LDO2,
.ops = &max77836_ldo_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
.min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
.uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
.enable_reg = MAX77836_LDO_REG_CNFG1_LDO2,
.enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
.vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2,
.vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
},
};
#ifdef CONFIG_OF
static struct of_regulator_match max14577_regulator_matches[] = {
{ .name = "SAFEOUT", },
{ .name = "CHARGER", },
};
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
static struct of_regulator_match max77836_regulator_matches[] = {
{ .name = "SAFEOUT", },
{ .name = "CHARGER", },
{ .name = "LDO1", },
{ .name = "LDO2", },
};
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
enum maxim_device_type dev_type)
{
int ret;
struct device_node *np;
struct of_regulator_match *regulator_matches;
unsigned int regulator_matches_size;
np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators");
if (!np) {
......@@ -158,8 +274,19 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
return -EINVAL;
}
ret = of_regulator_match(&pdev->dev, np, max14577_regulator_matches,
MAX14577_REG_MAX);
switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
regulator_matches = max77836_regulator_matches;
regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
regulator_matches = max14577_regulator_matches;
regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches);
}
ret = of_regulator_match(&pdev->dev, np, regulator_matches,
regulator_matches_size);
if (ret < 0)
dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
else
......@@ -170,31 +297,74 @@ static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
return ret;
}
static inline struct regulator_init_data *match_init_data(int index)
static inline struct regulator_init_data *match_init_data(int index,
enum maxim_device_type dev_type)
{
return max14577_regulator_matches[index].init_data;
switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
return max77836_regulator_matches[index].init_data;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577_regulator_matches[index].init_data;
}
}
static inline struct device_node *match_of_node(int index)
static inline struct device_node *match_of_node(int index,
enum maxim_device_type dev_type)
{
return max14577_regulator_matches[index].of_node;
switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
return max77836_regulator_matches[index].of_node;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577_regulator_matches[index].of_node;
}
}
#else /* CONFIG_OF */
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev)
static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev,
enum maxim_device_type dev_type)
{
return 0;
}
static inline struct regulator_init_data *match_init_data(int index)
static inline struct regulator_init_data *match_init_data(int index,
enum maxim_device_type dev_type)
{
return NULL;
}
static inline struct device_node *match_of_node(int index)
static inline struct device_node *match_of_node(int index,
enum maxim_device_type dev_type)
{
return NULL;
}
#endif /* CONFIG_OF */
/**
* Registers for regulators of max77836 use different I2C slave addresses so
* different regmaps must be used for them.
*
* Returns proper regmap for accessing regulator passed by id.
*/
static struct regmap *max14577_get_regmap(struct max14577 *max14577,
int reg_id)
{
switch (max14577->dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
switch (reg_id) {
case MAX77836_SAFEOUT ... MAX77836_CHARGER:
return max14577->regmap;
default:
/* MAX77836_LDO1 ... MAX77836_LDO2 */
return max14577->regmap_pmic;
}
case MAXIM_DEVICE_TYPE_MAX14577:
default:
return max14577->regmap;
}
}
static int max14577_regulator_probe(struct platform_device *pdev)
{
......@@ -202,15 +372,29 @@ static int max14577_regulator_probe(struct platform_device *pdev)
struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev);
int i, ret;
struct regulator_config config = {};
const struct regulator_desc *supported_regulators;
unsigned int supported_regulators_size;
enum maxim_device_type dev_type = max14577->dev_type;
ret = max14577_regulator_dt_parse_pdata(pdev);
ret = max14577_regulator_dt_parse_pdata(pdev, dev_type);
if (ret)
return ret;
switch (dev_type) {
case MAXIM_DEVICE_TYPE_MAX77836:
supported_regulators = max77836_supported_regulators;
supported_regulators_size = ARRAY_SIZE(max77836_supported_regulators);
break;
case MAXIM_DEVICE_TYPE_MAX14577:
default:
supported_regulators = max14577_supported_regulators;
supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators);
}
config.dev = &pdev->dev;
config.regmap = max14577->regmap;
config.driver_data = max14577;
for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
for (i = 0; i < supported_regulators_size; i++) {
struct regulator_dev *regulator;
/*
* Index of supported_regulators[] is also the id and must
......@@ -220,17 +404,19 @@ static int max14577_regulator_probe(struct platform_device *pdev)
config.init_data = pdata->regulators[i].initdata;
config.of_node = pdata->regulators[i].of_node;
} else {
config.init_data = match_init_data(i);
config.of_node = match_of_node(i);
config.init_data = match_init_data(i, dev_type);
config.of_node = match_of_node(i, dev_type);
}
config.regmap = max14577_get_regmap(max14577,
supported_regulators[i].id);
regulator = devm_regulator_register(&pdev->dev,
&supported_regulators[i], &config);
if (IS_ERR(regulator)) {
ret = PTR_ERR(regulator);
dev_err(&pdev->dev,
"Regulator init failed for ID %d with error: %d\n",
i, ret);
"Regulator init failed for %d/%s with error: %d\n",
i, supported_regulators[i].name, ret);
return ret;
}
}
......@@ -238,20 +424,41 @@ static int max14577_regulator_probe(struct platform_device *pdev)
return ret;
}
static const struct platform_device_id max14577_regulator_id[] = {
{ "max14577-regulator", MAXIM_DEVICE_TYPE_MAX14577, },
{ "max77836-regulator", MAXIM_DEVICE_TYPE_MAX77836, },
{ }
};
MODULE_DEVICE_TABLE(platform, max14577_regulator_id);
static struct platform_driver max14577_regulator_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "max14577-regulator",
},
.probe = max14577_regulator_probe,
.probe = max14577_regulator_probe,
.id_table = max14577_regulator_id,
};
static int __init max14577_regulator_init(void)
{
/* Check for valid values for charger */
BUILD_BUG_ON(MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_START +
MAX14577_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
MAX14577_REGULATOR_CURRENT_LIMIT_MAX);
BUILD_BUG_ON(ARRAY_SIZE(supported_regulators) != MAX14577_REG_MAX);
BUILD_BUG_ON(MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_START +
MAX77836_REGULATOR_CURRENT_LIMIT_HIGH_STEP * 0xf !=
MAX77836_REGULATOR_CURRENT_LIMIT_MAX);
/* Valid charger current values must be provided for each chipset */
BUILD_BUG_ON(ARRAY_SIZE(maxim_charger_currents) != MAXIM_DEVICE_TYPE_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max14577_supported_regulators) != MAX14577_REGULATOR_NUM);
BUILD_BUG_ON(ARRAY_SIZE(max77836_supported_regulators) != MAX77836_REGULATOR_NUM);
BUILD_BUG_ON(MAX77836_REGULATOR_LDO_VOLTAGE_MIN +
(MAX77836_REGULATOR_LDO_VOLTAGE_STEP *
(MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM - 1)) !=
MAX77836_REGULATOR_LDO_VOLTAGE_MAX);
return platform_driver_register(&max14577_regulator_driver);
}
......@@ -264,6 +471,6 @@ static void __exit max14577_regulator_exit(void)
module_exit(max14577_regulator_exit);
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
MODULE_DESCRIPTION("MAXIM 14577 regulator driver");
MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:max14577-regulator");
......@@ -655,7 +655,7 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
if (mem->addr & ~PAGE_MASK)
return -ENODEV;
if (vma->vm_end - vma->vm_start > mem->size)
if (vma->vm_end - vma->vm_start > PAGE_ALIGN(mem->size))
return -EINVAL;
vma->vm_ops = &uio_physical_vm_ops;
......
......@@ -204,7 +204,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
goto bad0;
goto bad1;
}
uioinfo->irq = ret;
}
......@@ -275,6 +275,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
ret = uio_register_device(&pdev->dev, priv->uioinfo);
if (ret) {
dev_err(&pdev->dev, "unable to register uio device\n");
pm_runtime_disable(&pdev->dev);
goto bad1;
}
......@@ -282,7 +283,6 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
return 0;
bad1:
kfree(priv);
pm_runtime_disable(&pdev->dev);
bad0:
/* kfree uioinfo for OF */
if (pdev->dev.of_node)
......
......@@ -1078,6 +1078,8 @@ static void w1_search_process(struct w1_master *dev, u8 search_type)
* w1_process_callbacks() - execute each dev->async_list callback entry
* @dev: w1_master device
*
* The w1 master list_mutex must be held.
*
* Return: 1 if there were commands to executed 0 otherwise
*/
int w1_process_callbacks(struct w1_master *dev)
......
......@@ -203,7 +203,6 @@ enum w1_master_flags {
* @search_id: allows continuing a search
* @refcnt: reference count
* @priv: private data storage
* @priv_size: size allocated
* @enable_pullup: allows a strong pullup
* @pullup_duration: time for the next strong pullup
* @flags: one of w1_master_flags
......@@ -214,7 +213,6 @@ enum w1_master_flags {
* @dev: sysfs device
* @bus_master: io operations available
* @seq: sequence number used for netlink broadcasts
* @portid: destination for the current netlink command
*/
struct w1_master
{
......@@ -241,7 +239,6 @@ struct w1_master
atomic_t refcnt;
void *priv;
int priv_size;
/** 5V strong pullup enabled flag, 1 enabled, zero disabled. */
int enable_pullup;
......@@ -260,11 +257,6 @@ struct w1_master
struct w1_bus_master *bus_master;
u32 seq;
/* port id to send netlink responses to. The value is temporarily
* stored here while processing a message, set after locking the
* mutex, zero before unlocking the mutex.
*/
u32 portid;
};
/**
......
......@@ -219,9 +219,13 @@ void __w1_remove_master_device(struct w1_master *dev)
if (msleep_interruptible(1000))
flush_signals(current);
mutex_lock(&dev->list_mutex);
w1_process_callbacks(dev);
mutex_unlock(&dev->list_mutex);
}
mutex_lock(&dev->list_mutex);
w1_process_callbacks(dev);
mutex_unlock(&dev->list_mutex);
memset(&msg, 0, sizeof(msg));
msg.id.mst.id = dev->id;
......
此差异已折叠。
......@@ -27,6 +27,17 @@
#include "w1.h"
/**
* enum w1_cn_msg_flags - bitfield flags for struct cn_msg.flags
*
* @W1_CN_BUNDLE: Request bundling replies into fewer messagse. Be prepared
* to handle multiple struct cn_msg, struct w1_netlink_msg, and
* struct w1_netlink_cmd in one packet.
*/
enum w1_cn_msg_flags {
W1_CN_BUNDLE = 1,
};
/**
* enum w1_netlink_message_types - message type
*
......@@ -49,6 +60,19 @@ enum w1_netlink_message_types {
W1_LIST_MASTERS,
};
/**
* struct w1_netlink_msg - holds w1 message type, id, and result
*
* @type: one of enum w1_netlink_message_types
* @status: kernel feedback for success 0 or errno failure value
* @len: length of data following w1_netlink_msg
* @id: union holding master bus id (msg.id) and slave device id (id[8]).
* @data: start address of any following data
*
* The base message structure for w1 messages over netlink.
* The netlink connector data sequence is, struct nlmsghdr, struct cn_msg,
* then one or more struct w1_netlink_msg (each with optional data).
*/
struct w1_netlink_msg
{
__u8 type;
......@@ -66,6 +90,7 @@ struct w1_netlink_msg
/**
* enum w1_commands - commands available for master or slave operations
*
* @W1_CMD_READ: read len bytes
* @W1_CMD_WRITE: write len bytes
* @W1_CMD_SEARCH: initiate a standard search, returns only the slave
......@@ -93,6 +118,17 @@ enum w1_commands {
W1_CMD_MAX
};
/**
* struct w1_netlink_cmd - holds the command and data
*
* @cmd: one of enum w1_commands
* @res: reserved
* @len: length of data following w1_netlink_cmd
* @data: start address of any following data
*
* One or more struct w1_netlink_cmd is placed starting at w1_netlink_msg.data
* each with optional data.
*/
struct w1_netlink_cmd
{
__u8 cmd;
......
......@@ -71,6 +71,7 @@ struct cn_dev {
int cn_add_callback(struct cb_id *id, const char *name,
void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
void cn_del_callback(struct cb_id *);
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 group, gfp_t gfp_mask);
int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 group, gfp_t gfp_mask);
int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
......
......@@ -185,8 +185,21 @@ struct extcon_specific_cable_nb {
*/
extern int extcon_dev_register(struct extcon_dev *edev);
extern void extcon_dev_unregister(struct extcon_dev *edev);
extern int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev);
extern void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev);
extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
/*
* Following APIs control the memory of extcon device.
*/
extern struct extcon_dev *extcon_dev_allocate(const char **cables);
extern void extcon_dev_free(struct extcon_dev *edev);
extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables);
extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev);
/*
* get/set/update_state access the 32b encoded state value, which represents
* states of all possible cables of the multistate port. For example, if one
......@@ -254,6 +267,30 @@ static inline int extcon_dev_register(struct extcon_dev *edev)
static inline void extcon_dev_unregister(struct extcon_dev *edev) { }
static inline int devm_extcon_dev_register(struct device *dev,
struct extcon_dev *edev)
{
return -EINVAL;
}
static inline void devm_extcon_dev_unregister(struct device *dev,
struct extcon_dev *edev) { }
static inline struct extcon_dev *extcon_dev_allocate(const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void extcon_dev_free(struct extcon_dev *edev) { }
static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
const char **cables)
{
return ERR_PTR(-ENOSYS);
}
static inline void devm_extcon_dev_free(struct extcon_dev *edev) { }
static inline u32 extcon_get_state(struct extcon_dev *edev)
{
return 0;
......
......@@ -696,6 +696,8 @@ struct vmbus_channel {
* preserve the earlier behavior.
*/
u32 target_vp;
/* The corresponding CPUID in the guest */
u32 target_cpu;
/*
* Support for sub-channels. For high performance devices,
* it will be useful to have multiple sub-channels to support
......@@ -732,6 +734,11 @@ struct vmbus_channel {
* Support per-channel state for use by vmbus drivers.
*/
void *per_channel_state;
/*
* To support per-cpu lookup mapping of relid to channel,
* link up channels based on their CPU affinity.
*/
struct list_head percpu_list;
};
static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
......
......@@ -16,6 +16,7 @@
#include <linux/irqreturn.h>
struct mcb_driver;
struct mcb_device;
/**
* struct mcb_bus - MEN Chameleon Bus
......@@ -23,11 +24,14 @@ struct mcb_driver;
* @dev: pointer to carrier device
* @children: the child busses
* @bus_nr: mcb bus number
* @get_irq: callback to get IRQ number
*/
struct mcb_bus {
struct list_head children;
struct device dev;
struct device *carrier;
int bus_nr;
int (*get_irq)(struct mcb_device *dev);
};
#define to_mcb_bus(b) container_of((b), struct mcb_bus, dev)
......@@ -105,7 +109,7 @@ extern void mcb_unregister_driver(struct mcb_driver *driver);
module_driver(__mcb_driver, mcb_register_driver, mcb_unregister_driver);
extern void mcb_bus_add_devices(const struct mcb_bus *bus);
extern int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev);
extern struct mcb_bus *mcb_alloc_bus(void);
extern struct mcb_bus *mcb_alloc_bus(struct device *carrier);
extern struct mcb_bus *mcb_bus_get(struct mcb_bus *bus);
extern void mcb_bus_put(struct mcb_bus *bus);
extern struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus);
......
/*
* max14577.h - Driver for the Maxim 14577
* max14577.h - Driver for the Maxim 14577/77836
*
* Copyright (C) 2013 Samsung Electrnoics
* Copyright (C) 2014 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
......@@ -20,6 +20,9 @@
* MAX14577 has MUIC, Charger devices.
* The devices share the same I2C bus and interrupt line
* included in this mfd driver.
*
* MAX77836 has additional PMIC and Fuel-Gauge on different I2C slave
* addresses.
*/
#ifndef __MAX14577_H__
......@@ -32,7 +35,17 @@ enum max14577_regulators {
MAX14577_SAFEOUT = 0,
MAX14577_CHARGER,
MAX14577_REG_MAX,
MAX14577_REGULATOR_NUM,
};
/* MAX77836 regulator IDs */
enum max77836_regulators {
MAX77836_SAFEOUT = 0,
MAX77836_CHARGER,
MAX77836_LDO1,
MAX77836_LDO2,
MAX77836_REGULATOR_NUM,
};
struct max14577_regulator_platform_data {
......
......@@ -415,7 +415,7 @@ struct palmas_usb {
struct palmas *palmas;
struct device *dev;
struct extcon_dev edev;
struct extcon_dev *edev;
int id_otg_irq;
int id_irq;
......
......@@ -64,7 +64,7 @@ struct miscdevice {
umode_t mode;
};
extern int misc_register(struct miscdevice * misc);
extern int misc_register(struct miscdevice *misc);
extern int misc_deregister(struct miscdevice *misc);
#define MODULE_ALIAS_MISCDEV(minor) \
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册