Skip to contentSkip to conversation

Intel MSIC MFD driver for Medfield platform

PATCH In review Mika Westerberg 2 participants 14 messages Sep 7, 2011 – Oct 20, 2011

Subsystems (2)
lore
Share & tools
lore
#7 lore raw

PATCH 3/3 x86, mrst: add platform support for MSIC MFD driver

In reply to Mika Westerberg

The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore.

This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly.

---
 arch/x86/platform/mrst/mrst.c |  167 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 153 insertions(+), 14 deletions(-)
arch/x86/platform/mrst/mrst.c +153 -14
index 97138a3..2a89f70 100644
@@ -25,6 +25,7 @@
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
+#include <linux/mfd/intel_msic.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -504,6 +505,128 @@ static void __init *no_platform_data(void *info)
 	return NULL;
 }
 
+static struct resource msic_resources[] = {
+	{
+		.start	= INTEL_MSIC_IRQ_PHYS_BASE,
+		.end	= INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct intel_msic_platform_data msic_pdata;
+
+static struct platform_device msic_device = {
+	.name		= "intel_msic",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &msic_pdata,
+	},
+	.num_resources	= ARRAY_SIZE(msic_resources),
+	.resource	= msic_resources,
+};
+
+static inline bool mrst_has_msic(void)
+{
+	return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL;
+}
+
+static int msic_scu_status_change(struct notifier_block *nb,
+				  unsigned long code, void *data)
+{
+	if (code == SCU_DOWN) {
+		platform_device_unregister(&msic_device);
+		return 0;
+	}
+
+	return platform_device_register(&msic_device);
+}
+
+static int __init msic_init(void)
+{
+	static struct notifier_block msic_scu_notifier = {
+		.notifier_call	= msic_scu_status_change,
+	};
+
+	/*
+	 * We need to be sure that the SCU IPC is ready before MSIC device
+	 * can be registered.
+	 */
+	if (mrst_has_msic())
+		intel_scu_notifier_add(&msic_scu_notifier);
+
+	return 0;
+}
+arch_initcall(msic_init);
+
+/*
+ * msic_generic_platform_data - sets generic platform data for the block
+ * @info: pointer to the SFI device table entry for this block
+ * @block: MSIC block
+ *
+ * Function sets IRQ number from the SFI table entry for given device to
+ * the MSIC platform data.
+ */
+static void *msic_generic_platform_data(void *info, enum intel_msic_block block)
+{
+	struct sfi_device_table_entry *entry = info;
+
+	BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST);
+	msic_pdata.irq[block] = entry->irq;
+
+	return no_platform_data(info);
+}
+
+static void *msic_battery_platform_data(void *info)
+{
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY);
+}
+
+static void *msic_gpio_platform_data(void *info)
+{
+	static struct intel_msic_gpio_pdata pdata;
+	int gpio = get_gpio_by_name("msic_gpio_base");
+
+	if (gpio < 0)
+		return NULL;
+
+	pdata.gpio_base = gpio;
+	msic_pdata.gpio = &pdata;
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO);
+}
+
+static void *msic_audio_platform_data(void *info)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("sst-platform", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		pr_err("failed to create audio platform device\n");
+		return NULL;
+	}
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO);
+}
+
+static void *msic_power_btn_platform_data(void *info)
+{
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN);
+}
+
+static void *msic_ocd_platform_data(void *info)
+{
+	static struct intel_msic_ocd_pdata pdata;
+	int gpio = get_gpio_by_name("ocd_gpio");
+
+	if (gpio < 0)
+		return NULL;
+
+	pdata.gpio = gpio;
+	msic_pdata.ocd = &pdata;
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD);
+}
+
 static const struct devs_id __initconst device_ids[] = {
 	{"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data},
 	{"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data},
@@ -512,7 +635,14 @@ static const struct devs_id __initconst device_ids[] = {
 	{"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data},
 	{"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data},
 	{"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
-	{"msic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
+
+	/* MSIC subdevices */
+	{"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data},
+	{"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data},
+	{"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data},
+	{"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data},
+	{"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data},
+
 	{},
 };
 
@@ -633,19 +763,37 @@ static void __init install_irq_resource(struct platform_device *pdev, int irq)
 	platform_device_add_resources(pdev, &res, 1);
 }
 
-static void __init sfi_handle_ipc_dev(struct platform_device *pdev)
+static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry)
 {
 	const struct devs_id *dev = device_ids;
+	struct platform_device *pdev;
 	void *pdata = NULL;
 
 	while (dev->name[0]) {
 		if (dev->type == SFI_DEV_TYPE_IPC &&
-			!strncmp(dev->name, pdev->name, SFI_NAME_LEN)) {
-			pdata = dev->get_platform_data(pdev);
+			!strncmp(dev->name, entry->name, SFI_NAME_LEN)) {
+			pdata = dev->get_platform_data(entry);
 			break;
 		}
 		dev++;
 	}
+
+	/*
+	 * On Medfield the platform device creation is handled by the MSIC
+	 * MFD driver so we don't need to do it here.
+	 */
+	if (mrst_has_msic())
+		return;
+
+	/* ID as IRQ is a hack that will go away */
+	pdev = platform_device_alloc(entry->name, entry->irq);
+	if (pdev == NULL) {
+		pr_err("out of memory for SFI platform device '%s'.\n",
+			entry->name);
+		return;
+	}
+	install_irq_resource(pdev, entry->irq);
+
 	pdev->dev.platform_data = pdata;
 	intel_scu_device_register(pdev);
 }
@@ -698,7 +846,6 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
 	struct sfi_device_table_entry *pentry;
 	struct spi_board_info spi_info;
 	struct i2c_board_info i2c_info;
-	struct platform_device *pdev;
 	int num, i, bus;
 	int ioapic;
 	struct io_apic_irq_attr irq_attr;
@@ -724,17 +871,9 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
 
 		switch (pentry->type) {
 		case SFI_DEV_TYPE_IPC:
-			/* ID as IRQ is a hack that will go away */
-			pdev = platform_device_alloc(pentry->name, pentry->irq);
-			if (pdev == NULL) {
-				pr_err("out of memory for SFI platform device '%s'.\n",
-							pentry->name);
-				continue;
-			}
-			install_irq_resource(pdev, pentry->irq);
 			pr_debug("info[%2d]: IPC bus, name = %16.16s, "
 				"irq = 0x%2x\n", i, pentry->name, pentry->irq);
-			sfi_handle_ipc_dev(pdev);
+			sfi_handle_ipc_dev(pentry);
 			break;
 		case SFI_DEV_TYPE_SPI:
 			memset(&spi_info, 0, sizeof(spi_info));
Signature · 1 line
1.7.5.4
#0 lore raw

cover Intel MSIC MFD driver for Medfield platform

Hi,

This series adds support for the Intel MSIC (Mixed Signal IC) chip found on Intel Medfield platforms. The MSIC chip embeds audio codec, ADC, GPIOs etc.

Existing drivers which use plain SCU IPC will continue to work and they can be converted to the MSIC register interface later on. Any new drivers for devices in the MSIC should use this interface.

Thanks.

Alan Cox (1):
x86, mrst: Some drivers need to known when an SCU is available

Mika Westerberg (2):
mfd: add Intel MSIC driver
x86, mrst: add platform support for MSIC MFD driver

 arch/x86/include/asm/intel_scu_ipc.h |   22 ++
 arch/x86/platform/mrst/mrst.c        |  174 +++++++++++-
 drivers/mfd/Kconfig                  |    9 +
 drivers/mfd/Makefile                 |    1 +
 drivers/mfd/intel_msic.c             |  501 ++++++++++++++++++++++++++++++++++
 include/linux/mfd/intel_msic.h       |  456 +++++++++++++++++++++++++++++++
 6 files changed, 1149 insertions(+), 14 deletions(-)
 create mode 100644 drivers/mfd/intel_msic.c
 create mode 100644 include/linux/mfd/intel_msic.h
Signature · 1 line
1.7.5.4
#6 lore raw

PATCH 2/3 x86, mrst: Some drivers need to known when an SCU is available

From: Alan Cox <[email protected]>

Add a notifier so that drivers can hook into SCU availability in order to take actions post initialisation when/if the SCU becomes available.

In the ideal world we wouldn't need this and we could avoid any init dependancies of this form, but in practice we can't do it for some cases.

---
 arch/x86/include/asm/intel_scu_ipc.h |   22 ++++++++++++++++++++++
 arch/x86/platform/mrst/mrst.c        |    7 +++++++
 2 files changed, 29 insertions(+), 0 deletions(-)
arch/x86/include/asm/intel_scu_ipc.h +22 -0
index 29f6679..4420993 100644
@@ -1,6 +1,8 @@
 #ifndef _ASM_X86_INTEL_SCU_IPC_H_
 #define  _ASM_X86_INTEL_SCU_IPC_H_
 
+#include <linux/notifier.h>
+
 #define IPCMSG_VRTC	0xFA	 /* Set vRTC device */
 
 /* Command id associated with message IPCMSG_VRTC */
@@ -44,4 +46,24 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
 /* Update FW version */
 int intel_scu_ipc_fw_update(u8 *buffer, u32 length);
 
+extern struct blocking_notifier_head intel_scu_notifier;
+
+static inline void intel_scu_notifier_add(struct notifier_block *nb)
+{
+	blocking_notifier_chain_register(&intel_scu_notifier, nb);
+}
+
+static inline void intel_scu_notifier_remove(struct notifier_block *nb)
+{
+	blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
+}
+
+static inline int intel_scu_notifier_post(unsigned long v, void *p)
+{
+	return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
+}
+
+#define		SCU_AVAILABLE		1
+#define		SCU_DOWN		2
+
 #endif
arch/x86/platform/mrst/mrst.c +7 -0
index 745db49..97138a3 100644
@@ -24,6 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/notifier.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -578,6 +579,9 @@ static void __init intel_scu_i2c_device_register(int bus,
 	i2c_devs[i2c_next_dev++] = new_dev;
 }
 
+BLOCKING_NOTIFIER_HEAD(intel_scu_notifier);
+EXPORT_SYMBOL_GPL(intel_scu_notifier);
+
 /* Called by IPC driver */
 void intel_scu_devices_create(void)
 {
@@ -602,6 +606,7 @@ void intel_scu_devices_create(void)
 		} else
 			i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1);
 	}
+	intel_scu_notifier_post(SCU_AVAILABLE, 0L);
 }
 EXPORT_SYMBOL_GPL(intel_scu_devices_create);
 
@@ -610,6 +615,8 @@ void intel_scu_devices_destroy(void)
 {
 	int i;
 
+	intel_scu_notifier_post(SCU_DOWN, 0L);
+
 	for (i = 0; i < ipc_next_dev; i++)
 		platform_device_del(ipc_devs[i]);
 }
Signature · 1 line
1.7.5.4
#1 lore raw

PATCH 1/3 mfd: add Intel MSIC driver

In reply to Mika Westerberg

Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice.

We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface.

---
 drivers/mfd/Kconfig            |    9 +
 drivers/mfd/Makefile           |    1 +
 drivers/mfd/intel_msic.c       |  501 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/intel_msic.h |  456 ++++++++++++++++++++++++++++++++++++
 4 files changed, 967 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/intel_msic.c
 create mode 100644 include/linux/mfd/intel_msic.h
drivers/mfd/Kconfig +9 -0
index 21574bd..3f02265 100644
@@ -770,6 +770,15 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_INTEL_MSIC
+	bool "Support for Intel MSIC"
+	depends on INTEL_SCU_IPC
+	select MFD_CORE
+	help
+	  Select this option to enable access to Intel MSIC (Avatele
+	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
+	  devices used in Intel Medfield platforms.
+
 endif # MFD_SUPPORT
 
 menu "Multimedia Capabilities Port drivers"
drivers/mfd/Makefile +1 -0
index c580203..7d53a7c 100644
@@ -102,3 +102,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o
 obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
drivers/mfd/intel_msic.c new file +501 -0

501 lines not on this page · raw message

include/linux/mfd/intel_msic.h new file +456 -0

456 lines not on this page · raw message

Signature · 1 line
1.7.5.4
#12 lore raw
In reply to Mika Westerberg

Hi Samuel and x86 maintainers,

Do you have any comments on these patches? Could they be merged into mainline or do you want me to make some changes?

Thanks.

Quoted 30 lines · Mika Westerberg

Hi,

This series adds support for the Intel MSIC (Mixed Signal IC) chip found on Intel Medfield platforms. The MSIC chip embeds audio codec, ADC, GPIOs etc.

Existing drivers which use plain SCU IPC will continue to work and they can be converted to the MSIC register interface later on. Any new drivers for devices in the MSIC should use this interface.

Thanks.

Alan Cox (1):
x86, mrst: Some drivers need to known when an SCU is available

Mika Westerberg (2):
mfd: add Intel MSIC driver
x86, mrst: add platform support for MSIC MFD driver

 arch/x86/include/asm/intel_scu_ipc.h |   22 ++
 arch/x86/platform/mrst/mrst.c        |  174 +++++++++++-
 drivers/mfd/Kconfig                  |    9 +
 drivers/mfd/Makefile                 |    1 +
 drivers/mfd/intel_msic.c             |  501 ++++++++++++++++++++++++++++++++++
 include/linux/mfd/intel_msic.h       |  456 +++++++++++++++++++++++++++++++
 6 files changed, 1149 insertions(+), 14 deletions(-)
 create mode 100644 drivers/mfd/intel_msic.c
 create mode 100644 include/linux/mfd/intel_msic.h
Signature · 1 line
1.7.5.4
#13 lore raw

Hi Mika,

Quoted 4 lines · Mika Westerberg

Hi Samuel and x86 maintainers,

Do you have any comments on these patches? Could they be merged into mainline or do you want me to make some changes?

I'll give my comments on the MFD parts later today or tomorrow.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#2 lore raw

Re: [PATCH 1/3] mfd: add Intel MSIC driver

In reply to Mika Westerberg

Hi Mika,

Quoted 7 lines · Mika Westerberg

Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice.

We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface.

Looks good overall, I just have a few comments/questions:

Quoted 3 lines

+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table.

Would that mean a broken firmware, or something else I'm missing ? It looks odd.

Quoted 21 lines
drivers/mfd/intel_msic.c new file +21 -0
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+	return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *

drivers/mfd/intel_msic.c line 217

Quoted 10 lines
drivers/mfd/intel_msic.c new file +10 -0
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+	return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);

What is the benefit of those wrappers since you're probably not going to get rid of the SCU IPC APIs, right ? What's wrong with the subdevices using the SCU IPC API directly ?

Quoted 33 lines
drivers/mfd/intel_msic.c new file +33 -0
+/**
+ * intel_msic_bulk_read - read an array of registers
+ * @reg: array of register addresses to read
+ * @buf: array where the read values are placed
+ * @count: number of registers to read
+ *
+ * Function reads @count registers from the MSIC using addresses passed in
+ * @reg. Read values are placed in @buf. Reads are performed atomically
+ * wrt. MSIC.
+ *
+ * Returns %0 in case of success and negative errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_readv(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
+
+/**
+ * intel_msic_bulk_write - write an array of values to the MSIC registers
+ * @reg: array of registers to write
+ * @buf: values to write to each register
+ * @count: number of registers to write
+ *
+ * Function writes @count registers in @buf to MSIC. Writes are performed
+ * atomically wrt MSIC. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *

drivers/mfd/intel_msic.c line 280

Quoted 10 lines
drivers/mfd/intel_msic.c new file +10 -0
+ * Function may sleep.
+ */
+int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_writev(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_write);

The 2 routines above make more sense.

Quoted 26 lines
drivers/mfd/intel_msic.c new file +26 -0
+static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+{
+	struct platform_device *pdev = msic->pdev;
+	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+	int ret, i;
+
+	if (pdata->gpio) {
+		struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
+
+		cell->platform_data = pdata->gpio;
+		cell->pdata_size = sizeof(*pdata->gpio);
+	}
+
+	if (pdata->ocd) {
+		unsigned gpio = pdata->ocd->gpio;
+
+		ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OCD GPIO\n");
+			return ret;
+		}
+
+		ret = gpio_to_irq(gpio);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
+			gpio_free(gpio);

drivers/mfd/intel_msic.c line 343

Quoted 10 lines
drivers/mfd/intel_msic.c new file +10 -0
+			return ret;
+		}
+
+		/* Update the IRQ number for the OCD */
+		pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+		if (!pdata->irq[i])
+			continue;

I would expect some sort of warning here since it would mean your platform code defined a sub device platform data without giving you the right IRQ. And afaiu, all of your sub devices must have one.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#3 lore raw

Re: [PATCH 1/3] mfd: add Intel MSIC driver

Quoted 11 lines · Samuel Ortiz

Hi Mika,

Quoted 7 lines · Mika Westerberg

Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice.

We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface.

Looks good overall, I just have a few comments/questions:

Thanks for your comments. I've tried to answer them below.

Quoted 5 lines
Quoted 3 lines

+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table.

Would that mean a broken firmware, or something else I'm missing ? It looks odd.

Not all devices in the MSIC are listed in SFI DEVS table. For example regulators are missing. We currently don't have regulator support for MSIC but once it is added the devices will be created here.

Audio codec is similar - it does not exist in the SFI DEVS table.

Quoted 26 lines
Quoted 26 lines
drivers/mfd/intel_msic.c new file +26 -0
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+	return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
Quoted 8 lines

drivers/mfd/intel_msic.c line 217

Quoted 5 lines
drivers/mfd/intel_msic.c new file +5 -0
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+	return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);

What is the benefit of those wrappers since you're probably not going to get rid of the SCU IPC APIs, right ? What's wrong with the subdevices using the SCU IPC API directly ?

Right, we are not going to get rid of SCU IPC APIs. The idea behind having these register access wrapper functions is that we are now able to separate the MSIC subdevices from other SCU IPC usage. In other words we get a bit cleaner "architecture".

It also allows us to add caching and intercepting register accesses if a need rises.

Quoted 36 lines
Quoted 36 lines
drivers/mfd/intel_msic.c new file +36 -0
+/**
+ * intel_msic_bulk_read - read an array of registers
+ * @reg: array of register addresses to read
+ * @buf: array where the read values are placed
+ * @count: number of registers to read
+ *
+ * Function reads @count registers from the MSIC using addresses passed in
+ * @reg. Read values are placed in @buf. Reads are performed atomically
+ * wrt. MSIC.
+ *
+ * Returns %0 in case of success and negative errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count)
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_readv(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_read);
+
+/**
+ * intel_msic_bulk_write - write an array of values to the MSIC registers
+ * @reg: array of registers to write
+ * @buf: values to write to each register
+ * @count: number of registers to write
+ *
+ * Function writes @count registers in @buf to MSIC. Writes are performed
+ * atomically wrt MSIC. Returns %0 in case of success and negative errno in
+ * case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count)
Quoted 8 lines

drivers/mfd/intel_msic.c line 280

Quoted 7 lines
drivers/mfd/intel_msic.c new file +7 -0
+{
+	if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT))
+		return -EINVAL;
+
+	return intel_scu_ipc_writev(reg, buf, count);
+}
+EXPORT_SYMBOL_GPL(intel_msic_bulk_write);

The 2 routines above make more sense.

Ok.

Quoted 31 lines
Quoted 31 lines
drivers/mfd/intel_msic.c new file +31 -0
+static int __devinit intel_msic_init_devices(struct intel_msic *msic)
+{
+	struct platform_device *pdev = msic->pdev;
+	struct intel_msic_platform_data *pdata = pdev->dev.platform_data;
+	int ret, i;
+
+	if (pdata->gpio) {
+		struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO];
+
+		cell->platform_data = pdata->gpio;
+		cell->pdata_size = sizeof(*pdata->gpio);
+	}
+
+	if (pdata->ocd) {
+		unsigned gpio = pdata->ocd->gpio;
+
+		ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio");
+		if (ret) {
+			dev_err(&pdev->dev, "failed to register OCD GPIO\n");
+			return ret;
+		}
+
+		ret = gpio_to_irq(gpio);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n");
+			gpio_free(gpio);
+			return ret;
+		}
+
+		/* Update the IRQ number for the OCD */
+		pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret;
Quoted 8 lines

drivers/mfd/intel_msic.c line 343

Quoted 5 lines
drivers/mfd/intel_msic.c new file +5 -0
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+		if (!pdata->irq[i])
+			continue;

I would expect some sort of warning here since it would mean your platform code defined a sub device platform data without giving you the right IRQ. And afaiu, all of your sub devices must have one.

The interface for platform data says that if irq is zero, it means that no platform device is created.

For example in patch 3/3 we add the platform data for battery, gpio, audio, power_button and ocd but the SFI table actually contains more devices. We don't yet have (upstream) driver for those so there is a little point of creating platform device for them at this point.

#4 lore raw

Re: [PATCH 1/3] mfd: add Intel MSIC driver

Hi Mika,

Quoted 11 lines · Mika Westerberg
Quoted 5 lines
Quoted 3 lines

+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table.

Would that mean a broken firmware, or something else I'm missing ? It looks odd.

Not all devices in the MSIC are listed in SFI DEVS table. For example regulators are missing. We currently don't have regulator support for MSIC but once it is added the devices will be created here.

Audio codec is similar - it does not exist in the SFI DEVS table.

Ok. So I can expect further patches removing this one then.

Quoted 35 lines
Quoted 26 lines
Quoted 26 lines
drivers/mfd/intel_msic.c new file +26 -0
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+	return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
Quoted 8 lines

drivers/mfd/intel_msic.c line 217

Quoted 5 lines
drivers/mfd/intel_msic.c new file +5 -0
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+	return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);

What is the benefit of those wrappers since you're probably not going to get rid of the SCU IPC APIs, right ? What's wrong with the subdevices using the SCU IPC API directly ?

Quoted 4 lines

Right, we are not going to get rid of SCU IPC APIs. The idea behind having these register access wrapper functions is that we are now able to separate the MSIC subdevices from other SCU IPC usage. In other words we get a bit cleaner "architecture".

I'm not entirely convinced that wrapping around the IPC API gives you a cleaner architecture.

Quoted 2 lines

It also allows us to add caching and intercepting register accesses if a need rises.

That would define a real need for this API, yes.

Quoted 7 lines
Quoted 6 lines

drivers/mfd/intel_msic.c line 343

Quoted 3 lines
drivers/mfd/intel_msic.c new file +3 -0
+	for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+		if (!pdata->irq[i])
+			continue;

I would expect some sort of warning here since it would mean your platform code defined a sub device platform data without giving you the right IRQ. And afaiu, all of your sub devices must have one.

Quoted 7 lines

The interface for platform data says that if irq is zero, it means that no platform device is created.

For example in patch 3/3 we add the platform data for battery, gpio, audio, power_button and ocd but the SFI table actually contains more devices. We don't yet have (upstream) driver for those so there is a little point of creating platform device for them at this point.

Ok, I see.

So, I'm fine with this patch, at least the MFD part. I'll go ahead and apply it to my for-next branch. I'd appreciate to get ACKs or NACKs for the x86 parts from the relevant people though.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#5 lore raw

Re: [PATCH 1/3] mfd: add Intel MSIC driver

Quoted 9 lines · Samuel Ortiz

Hi Mika,

Quoted 6 lines · Mika Westerberg
Quoted 5 lines
Quoted 3 lines

+/*
+ * Other MSIC related devices which are not directly available via SFI DEVS
+ * table.

Would that mean a broken firmware, or something else I'm missing ? It looks odd.

Quoted 6 lines
Quoted 5 lines

Not all devices in the MSIC are listed in SFI DEVS table. For example regulators are missing. We currently don't have regulator support for MSIC but once it is added the devices will be created here.

Audio codec is similar - it does not exist in the SFI DEVS table.

Ok. So I can expect further patches removing this one then.

For the regulators and audio codec, I'm not sure whether we ever get those added to the SFI table. But we know that they will be embedded in the MSIC chip.

It might be the case that we need to add there more stuff - regulators come into mind when we get driver for them.

Quoted 36 lines
Quoted 36 lines
Quoted 35 lines
Quoted 22 lines
drivers/mfd/intel_msic.c new file +21 -0
+/**
+ * intel_msic_reg_read - read a single MSIC register
+ * @reg: register to read
+ * @val: register value is placed here
+ *
+ * Read a single register from MSIC. Returns %0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_read(unsigned short reg, u8 *val)
+{
+	return intel_scu_ipc_ioread8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_read);
+
+/**
+ * intel_msic_reg_write - write a single MSIC register
+ * @reg: register to write
+ * @val: value to write to that register
+ *

drivers/mfd/intel_msic.c line 217

Quoted 10 lines
drivers/mfd/intel_msic.c new file +10 -0
+ * Write a single MSIC register. Returns 0 on success and negative
+ * errno in case of failure.
+ *
+ * Function may sleep.
+ */
+int intel_msic_reg_write(unsigned short reg, u8 val)
+{
+	return intel_scu_ipc_iowrite8(reg, val);
+}
+EXPORT_SYMBOL_GPL(intel_msic_reg_write);

What is the benefit of those wrappers since you're probably not going to get rid of the SCU IPC APIs, right ? What's wrong with the subdevices using the SCU IPC API directly ?

Quoted 6 lines
Quoted 4 lines

Right, we are not going to get rid of SCU IPC APIs. The idea behind having these register access wrapper functions is that we are now able to separate the MSIC subdevices from other SCU IPC usage. In other words we get a bit cleaner "architecture".

I'm not entirely convinced that wrapping around the IPC API gives you a cleaner architecture.

Ok.

Quoted 5 lines
Quoted 4 lines

It also allows us to add caching and intercepting register accesses if a need rises.

That would define a real need for this API, yes.

Ok.

Quoted 17 lines
Quoted 9 lines
Quoted 8 lines

drivers/mfd/intel_msic.c line 343

Quoted 5 lines
drivers/mfd/intel_msic.c new file +3 -0
+	for (i = 0; i < ARRAY_SIZE(msic_devs); i++) {
+		if (!pdata->irq[i])
+			continue;

I would expect some sort of warning here since it would mean your platform code defined a sub device platform data without giving you the right IRQ. And afaiu, all of your sub devices must have one.

Quoted 7 lines

The interface for platform data says that if irq is zero, it means that no platform device is created.

For example in patch 3/3 we add the platform data for battery, gpio, audio, power_button and ocd but the SFI table actually contains more devices. We don't yet have (upstream) driver for those so there is a little point of creating platform device for them at this point.

Quoted 5 lines

Ok, I see.

So, I'm fine with this patch, at least the MFD part. I'll go ahead and apply it to my for-next branch. I'd appreciate to get ACKs or NACKs for the x86 parts from the relevant people though.

Thanks.

I would also like to hear what x86 maintainers think about those parts. Hmm, is there some mailing list which I missed or did forgot to Cc someone?

#8 lore raw

Re: [PATCH 3/3] x86, mrst: add platform support for MSIC MFD driver

In reply to Mika Westerberg

Hi Samuel,

Quoted 5 lines · Mika Westerberg

The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore.

This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly.

There is a fix in 3.1 which conflicts this patch. The fix is 153b19a3b9fd8b (x86, mrst: use a temporary variable for SFI irq) which is needed for Medfield platforms to get kexec working.

If you like, I can send you an updated version of this patch which doesn't conflict. Please let me know.

#9 lore raw

Re: [PATCH 3/3] x86, mrst: add platform support for MSIC MFD driver

Hi Mika,

Quoted 9 lines · Mika Westerberg

Hi Samuel,

Quoted 5 lines · Mika Westerberg

The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore.

This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly.

Quoted 6 lines

There is a fix in 3.1 which conflicts this patch. The fix is 153b19a3b9fd8b (x86, mrst: use a temporary variable for SFI irq) which is needed for Medfield platforms to get kexec working.

If you like, I can send you an updated version of this patch which doesn't conflict.

I would appreciate that, please send it to me.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/
#10 lore raw

PATCH v2 1/1 x86, mrst: add platform support for MSIC MFD driver

The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore.

This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly.

---
 arch/x86/platform/mrst/mrst.c |  169 +++++++++++++++++++++++++++++++++++++----
 1 files changed, 154 insertions(+), 15 deletions(-)
arch/x86/platform/mrst/mrst.c +154 -15
index 51ec016..8a7cb32 100644
@@ -25,6 +25,7 @@
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
+#include <linux/mfd/intel_msic.h>
 
 #include <asm/setup.h>
 #include <asm/mpspec_def.h>
@@ -481,6 +482,128 @@ static void __init *no_platform_data(void *info)
 	return NULL;
 }
 
+static struct resource msic_resources[] = {
+	{
+		.start	= INTEL_MSIC_IRQ_PHYS_BASE,
+		.end	= INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct intel_msic_platform_data msic_pdata;
+
+static struct platform_device msic_device = {
+	.name		= "intel_msic",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &msic_pdata,
+	},
+	.num_resources	= ARRAY_SIZE(msic_resources),
+	.resource	= msic_resources,
+};
+
+static inline bool mrst_has_msic(void)
+{
+	return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL;
+}
+
+static int msic_scu_status_change(struct notifier_block *nb,
+				  unsigned long code, void *data)
+{
+	if (code == SCU_DOWN) {
+		platform_device_unregister(&msic_device);
+		return 0;
+	}
+
+	return platform_device_register(&msic_device);
+}
+
+static int __init msic_init(void)
+{
+	static struct notifier_block msic_scu_notifier = {
+		.notifier_call	= msic_scu_status_change,
+	};
+
+	/*
+	 * We need to be sure that the SCU IPC is ready before MSIC device
+	 * can be registered.
+	 */
+	if (mrst_has_msic())
+		intel_scu_notifier_add(&msic_scu_notifier);
+
+	return 0;
+}
+arch_initcall(msic_init);
+
+/*
+ * msic_generic_platform_data - sets generic platform data for the block
+ * @info: pointer to the SFI device table entry for this block
+ * @block: MSIC block
+ *
+ * Function sets IRQ number from the SFI table entry for given device to
+ * the MSIC platform data.
+ */
+static void *msic_generic_platform_data(void *info, enum intel_msic_block block)
+{
+	struct sfi_device_table_entry *entry = info;
+
+	BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST);
+	msic_pdata.irq[block] = entry->irq;
+
+	return no_platform_data(info);
+}
+
+static void *msic_battery_platform_data(void *info)
+{
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY);
+}
+
+static void *msic_gpio_platform_data(void *info)
+{
+	static struct intel_msic_gpio_pdata pdata;
+	int gpio = get_gpio_by_name("msic_gpio_base");
+
+	if (gpio < 0)
+		return NULL;
+
+	pdata.gpio_base = gpio;
+	msic_pdata.gpio = &pdata;
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO);
+}
+
+static void *msic_audio_platform_data(void *info)
+{
+	struct platform_device *pdev;
+
+	pdev = platform_device_register_simple("sst-platform", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		pr_err("failed to create audio platform device\n");
+		return NULL;
+	}
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO);
+}
+
+static void *msic_power_btn_platform_data(void *info)
+{
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN);
+}
+
+static void *msic_ocd_platform_data(void *info)
+{
+	static struct intel_msic_ocd_pdata pdata;
+	int gpio = get_gpio_by_name("ocd_gpio");
+
+	if (gpio < 0)
+		return NULL;
+
+	pdata.gpio = gpio;
+	msic_pdata.ocd = &pdata;
+
+	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD);
+}
+
 static const struct devs_id __initconst device_ids[] = {
 	{"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data},
 	{"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data},
@@ -489,7 +612,14 @@ static const struct devs_id __initconst device_ids[] = {
 	{"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data},
 	{"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data},
 	{"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
-	{"msic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data},
+
+	/* MSIC subdevices */
+	{"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data},
+	{"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data},
+	{"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data},
+	{"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data},
+	{"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data},
+
 	{},
 };
 
@@ -610,19 +740,37 @@ static void __init install_irq_resource(struct platform_device *pdev, int irq)
 	platform_device_add_resources(pdev, &res, 1);
 }
 
-static void __init sfi_handle_ipc_dev(struct platform_device *pdev)
+static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry)
 {
 	const struct devs_id *dev = device_ids;
+	struct platform_device *pdev;
 	void *pdata = NULL;
 
 	while (dev->name[0]) {
 		if (dev->type == SFI_DEV_TYPE_IPC &&
-			!strncmp(dev->name, pdev->name, SFI_NAME_LEN)) {
-			pdata = dev->get_platform_data(pdev);
+			!strncmp(dev->name, entry->name, SFI_NAME_LEN)) {
+			pdata = dev->get_platform_data(entry);
 			break;
 		}
 		dev++;
 	}
+
+	/*
+	 * On Medfield the platform device creation is handled by the MSIC
+	 * MFD driver so we don't need to do it here.
+	 */
+	if (mrst_has_msic())
+		return;
+
+	/* ID as IRQ is a hack that will go away */
+	pdev = platform_device_alloc(entry->name, entry->irq);
+	if (pdev == NULL) {
+		pr_err("out of memory for SFI platform device '%s'.\n",
+			entry->name);
+		return;
+	}
+	install_irq_resource(pdev, entry->irq);
+
 	pdev->dev.platform_data = pdata;
 	intel_scu_device_register(pdev);
 }
@@ -675,7 +823,6 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
 	struct sfi_device_table_entry *pentry;
 	struct spi_board_info spi_info;
 	struct i2c_board_info i2c_info;
-	struct platform_device *pdev;
 	int num, i, bus;
 	int ioapic;
 	struct io_apic_irq_attr irq_attr;
@@ -703,17 +850,9 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
 
 		switch (pentry->type) {
 		case SFI_DEV_TYPE_IPC:
-			/* ID as IRQ is a hack that will go away */
-			pdev = platform_device_alloc(pentry->name, irq);
-			if (pdev == NULL) {
-				pr_err("out of memory for SFI platform device '%s'.\n",
-							pentry->name);
-				continue;
-			}
-			install_irq_resource(pdev, irq);
 			pr_debug("info[%2d]: IPC bus, name = %16.16s, "
-				"irq = 0x%2x\n", i, pentry->name, irq);
-			sfi_handle_ipc_dev(pdev);
+				"irq = 0x%2x\n", i, pentry->name, pentry->irq);
+			sfi_handle_ipc_dev(pentry);
 			break;
 		case SFI_DEV_TYPE_SPI:
 			memset(&spi_info, 0, sizeof(spi_info));
Signature · 1 line
1.7.5.4
Applied
#11 lore raw

Re: [PATCH v2] x86, mrst: add platform support for MSIC MFD driver

Hi Mika,

Quoted 7 lines · Mika Westerberg

The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore.

This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly.

I applied this new version, thanks.

Cheers,
Samuel.

Signature · 2 lines
Intel Open Source Technology Centre
http://oss.intel.com/