* [PATCH V6 3/4] drm/bridge: Add driver for GE B850v3 LVDS/DP++ Bridge
From: Philipp Zabel @ 2016-11-01 11:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cbdd043a504a10a8f77fedb25d0f62dd8b837aca.1477572814.git.peter.senna@collabora.com>
Hi Peter,
Am Donnerstag, den 27.10.2016, 15:01 +0200 schrieb Peter Senna Tschudin:
> Add a driver that create a drm_bridge and a drm_connector for the LVDS
> to DP++ display bridge of the GE B850v3.
>
> There are two physical bridges on the video signal pipeline: a
> STDP4028(LVDS to DP) and a STDP2690(DP to DP++). The hardware and
> firmware made it complicated for this binding to comprise two device
> tree nodes, as the design goal is to configure both bridges based on
> the LVDS signal, which leave the driver powerless to control the video
> processing pipeline. The two bridges behaves as a single bridge, and
> the driver is only needed for telling the host about EDID / HPD, and
> for giving the host powers to ack interrupts. The video signal pipeline
> is as follows:
>
> Host -> LVDS|--(STDP4028)--|DP -> DP|--(STDP2690)--|DP++ -> Video output
>
> Cc: Martyn Welch <martyn.welch@collabora.co.uk>
> Cc: Martin Donnelly <martin.donnelly@ge.com>
> Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
> Cc: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Cc: Philipp Zabel <p.zabel@pengutronix.de>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Fabio Estevam <fabio.estevam@nxp.com>
> CC: David Airlie <airlied@linux.ie>
> CC: Thierry Reding <treding@nvidia.com>
> CC: Thierry Reding <thierry.reding@gmail.com>
> CC: Archit Taneja <architt@codeaurora.org>
> Reviewed-by: Enric Balletbo <enric.balletbo@collabora.com>
> Signed-off-by: Peter Senna Tschudin <peter.senna@collabora.com>
> ---
> Changes from V5:
> - Reworked interrupt handler initialization
> - Removed useless calls to: drm_connector_register(),
> drm_helper_hpd_irq_event(), and drm_bridge_enable()
>
> Changes from V4:
> - Renamed the i2c_driver.name from "ge,b850v3-lvds-dp" to "b850v3-lvds-dp" to
> remove the comma from the driver name
>
> Changes from V3:
> - 3/4 instead of 4/5
> - Tested on next-20160804
>
> Changes from V2:
> - Made it atomic to be applied on next-20160729 on top of Liu Ying changes
> that made imx-ldb atomic
>
> Changes from V1:
> - New commit message
> - Removed 3 empty entry points
> - Removed memory leak from ge_b850v3_lvds_dp_get_modes()
> - Added a lock for mode setting
> - Removed a few blank lines
> - Changed the order at Makefile and Kconfig
>
> drivers/gpu/drm/bridge/Kconfig | 11 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/ge_b850v3_lvds_dp.c | 395 +++++++++++++++++++++++++++++
> 3 files changed, 407 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/ge_b850v3_lvds_dp.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index bd6acc8..1d02422 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -39,6 +39,17 @@ config DRM_DW_HDMI_AHB_AUDIO
> Designware HDMI block. This is used in conjunction with
> the i.MX6 HDMI driver.
>
> +config DRM_GE_B850V3_LVDS_DP
> + tristate "GE B850v3 LVDS to DP++ display bridge"
> + depends on OF
> + select DRM_KMS_HELPER
> + select DRM_PANEL
> + ---help---
> + This is a driver for the display bridge of
> + GE B850v3 that convert dual channel LVDS
> + to DP++. This is used with the i.MX6 imx-ldb
> + driver.
> +
> config DRM_NXP_PTN3460
> tristate "NXP PTN3460 DP/LVDS bridge"
> depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 97ed1a5..b6b44a5 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
> obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
> obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
> obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> +obj-$(CONFIG_DRM_GE_B850V3_LVDS_DP) += ge_b850v3_lvds_dp.o
> obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
> obj-$(CONFIG_DRM_SII902X) += sii902x.o
> diff --git a/drivers/gpu/drm/bridge/ge_b850v3_lvds_dp.c b/drivers/gpu/drm/bridge/ge_b850v3_lvds_dp.c
> new file mode 100644
> index 0000000..85875d8
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ge_b850v3_lvds_dp.c
> @@ -0,0 +1,395 @@
> +/*
> + * Driver for GE B850v3 DP display bridge
> +
> + * Copyright (c) 2016, Collabora Ltd.
> + * Copyright (c) 2016, General Electric Company
> +
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> +
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> + * This driver creates a drm_bridge and a drm_connector for the LVDS to DP++
> + * display bridge of the GE B850v3. There are two physical bridges on the video
> + * signal pipeline: a STDP4028(LVDS to DP) and a STDP2690(DP to DP++). However
> + * the physical bridges are automatically configured by the input video signal,
> + * and the driver has no access to the video processing pipeline. The driver is
> + * only needed to read EDID from the STDP2690 and to handle HPD events from the
> + * STDP4028. The driver communicates with both bridges over i2c. The video
> + * signal pipeline is as follows:
> + *
> + * Host -> LVDS|--(STDP4028)--|DP -> DP|--(STDP2690)--|DP++ -> Video output
> + *
> + */
> +
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drmP.h>
> +
> +/*
> + * 220Mhz is a limitation of the host, as the bridge is capable of up to
> + * 330Mhz. See section 9.2.1.2.4 of the i.MX 6Dual/6Quad Applications
> + * Processor Reference Manual for more information about the 220Mhz limit.
> + * The imx-ldb driver will warn about clocks over 170Mhz, but it seem to work
> + * fine.
> + */
> +#define MAX_PIXEL_CLOCK 220000
That is a limitation of the IPU DI pixel clock rate if both DIs are
active. If only a single DI is active, this limit is 264 MHz on i.MX6Q.
The LDB pixel clock rate maximum is 170 MHz if only a single LDB channel
is active.
I currently see no way for the crtc and encoder code to have a say about
maximum pixel clocks when pruning the mode list in .fill_modes.
> +
> +#define EDID_EXT_BLOCK_CNT 0x7E
> +
> +#define STDP4028_IRQ_OUT_CONF_REG 0x02
> +#define STDP4028_DPTX_IRQ_EN_REG 0x3C
> +#define STDP4028_DPTX_IRQ_STS_REG 0x3D
> +#define STDP4028_DPTX_STS_REG 0x3E
> +
> +#define STDP4028_DPTX_DP_IRQ_EN 0x1000
> +
> +#define STDP4028_DPTX_HOTPLUG_IRQ_EN 0x0400
> +#define STDP4028_DPTX_LINK_CH_IRQ_EN 0x2000
> +#define STDP4028_DPTX_IRQ_CONFIG \
> + (STDP4028_DPTX_LINK_CH_IRQ_EN | STDP4028_DPTX_HOTPLUG_IRQ_EN)
> +
> +#define STDP4028_DPTX_HOTPLUG_STS 0x0200
> +#define STDP4028_DPTX_LINK_STS 0x1000
> +#define STDP4028_CON_STATE_CONNECTED \
> + (STDP4028_DPTX_HOTPLUG_STS | STDP4028_DPTX_LINK_STS)
> +
> +#define STDP4028_DPTX_HOTPLUG_CH_STS 0x0400
> +#define STDP4028_DPTX_LINK_CH_STS 0x2000
> +#define STDP4028_DPTX_IRQ_CLEAR \
> + (STDP4028_DPTX_LINK_CH_STS | STDP4028_DPTX_HOTPLUG_CH_STS)
> +
> +struct ge_b850v3_lvds_dp {
> + struct drm_connector connector;
> + struct drm_bridge bridge;
> + struct i2c_client *ge_b850v3_lvds_dp_i2c;
> + struct i2c_client *edid_i2c;
> + struct edid *edid;
> + struct mutex lock;
> +};
> +
> +static inline struct ge_b850v3_lvds_dp *
> + bridge_to_ge_b850v3_lvds_dp(struct drm_bridge *bridge)
> +{
> + return container_of(bridge, struct ge_b850v3_lvds_dp, bridge);
> +}
> +
> +static inline struct ge_b850v3_lvds_dp *
> + connector_to_ge_b850v3_lvds_dp(struct drm_connector *connector)
> +{
> + return container_of(connector, struct ge_b850v3_lvds_dp, connector);
> +}
> +
> +u8 *stdp2690_get_edid(struct i2c_client *client)
> +{
> + struct i2c_adapter *adapter = client->adapter;
> + unsigned char start = 0x00;
> + unsigned int total_size;
> + u8 *block = kmalloc(EDID_LENGTH, GFP_KERNEL);
> +
> + struct i2c_msg msgs[] = {
> + {
> + .addr = client->addr,
> + .flags = 0,
> + .len = 1,
> + .buf = &start,
> + }, {
> + .addr = client->addr,
> + .flags = I2C_M_RD,
> + .len = EDID_LENGTH,
> + .buf = block,
> + }
> + };
> +
> + if (!block)
> + return NULL;
> +
> + if (i2c_transfer(adapter, msgs, 2) != 2) {
> + DRM_ERROR("Unable to read EDID.\n");
> + goto err;
> + }
> +
> + if (!drm_edid_block_valid(block, 0, false, NULL)) {
> + DRM_ERROR("Invalid EDID block\n");
> + goto err;
> + }
> +
> + total_size = (block[EDID_EXT_BLOCK_CNT] + 1) * EDID_LENGTH;
> + if (total_size > EDID_LENGTH) {
> + kfree(block);
> + block = kmalloc(total_size, GFP_KERNEL);
> + if (!block)
> + return NULL;
> +
> + /* Yes, read the entire buffer, and do not skip the first
> + * EDID_LENGTH bytes.
> + */
> + start = 0x00;
> + msgs[1].len = total_size;
> + msgs[1].buf = block;
> +
> + if (i2c_transfer(adapter, msgs, 2) != 2) {
> + DRM_ERROR("Unable to read EDID extension blocks.\n");
> + goto err;
> + }
> + }
> +
> + return block;
> +
> +err:
> + kfree(block);
> + return NULL;
> +}
> +
> +static int ge_b850v3_lvds_dp_get_modes(struct drm_connector *connector)
> +{
> + struct ge_b850v3_lvds_dp *ptn_bridge;
> + struct i2c_client *client;
> + int num_modes = 0;
> +
> + ptn_bridge = connector_to_ge_b850v3_lvds_dp(connector);
> + client = ptn_bridge->edid_i2c;
> +
> + mutex_lock(&ptn_bridge->lock);
What does this lock protect?
> + kfree(ptn_bridge->edid);
> + ptn_bridge->edid = (struct edid *) stdp2690_get_edid(client);
> +
> + if (ptn_bridge->edid) {
> + drm_mode_connector_update_edid_property(connector,
> + ptn_bridge->edid);
> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
> + }
> +
> + mutex_unlock(&ptn_bridge->lock);
> +
> + return num_modes;
> +}
> +
> +
> +static enum drm_mode_status ge_b850v3_lvds_dp_mode_valid(
> + struct drm_connector *connector, struct drm_display_mode *mode)
> +{
> + if (mode->clock > MAX_PIXEL_CLOCK) {
> + DRM_INFO("The pixel clock for the mode %s is too high, and not supported.",
> + mode->name);
> + return MODE_CLOCK_HIGH;
> + }
> +
> + return MODE_OK;
> +}
> +
> +static const struct
> +drm_connector_helper_funcs ge_b850v3_lvds_dp_connector_helper_funcs = {
> + .get_modes = ge_b850v3_lvds_dp_get_modes,
> + .mode_valid = ge_b850v3_lvds_dp_mode_valid,
> +};
> +
> +static enum drm_connector_status ge_b850v3_lvds_dp_detect(
> + struct drm_connector *connector, bool force)
> +{
> + struct ge_b850v3_lvds_dp *ptn_bridge =
> + connector_to_ge_b850v3_lvds_dp(connector);
> + struct i2c_client *ge_b850v3_lvds_dp_i2c =
> + ptn_bridge->ge_b850v3_lvds_dp_i2c;
> + s32 link_state;
> +
> + link_state = i2c_smbus_read_word_data(ge_b850v3_lvds_dp_i2c,
> + STDP4028_DPTX_STS_REG);
> +
> + if (link_state == STDP4028_CON_STATE_CONNECTED)
> + return connector_status_connected;
> +
> + if (link_state == 0)
> + return connector_status_disconnected;
> +
> + return connector_status_unknown;
> +}
> +
> +static const struct drm_connector_funcs ge_b850v3_lvds_dp_connector_funcs = {
> + .dpms = drm_atomic_helper_connector_dpms,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = ge_b850v3_lvds_dp_detect,
> + .destroy = drm_connector_cleanup,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static irqreturn_t ge_b850v3_lvds_dp_irq_handler(int irq, void *dev_id)
> +{
> + struct ge_b850v3_lvds_dp *ptn_bridge = dev_id;
> + struct i2c_client *ge_b850v3_lvds_dp_i2c
> + = ptn_bridge->ge_b850v3_lvds_dp_i2c;
> +
> + mutex_lock(&ptn_bridge->lock);
And what does this lock protect here?
> + i2c_smbus_write_word_data(ge_b850v3_lvds_dp_i2c,
> + STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR);
> +
> + mutex_unlock(&ptn_bridge->lock);
> +
> + if (ptn_bridge->connector.dev)
> + drm_kms_helper_hotplug_event(ptn_bridge->connector.dev);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int ge_b850v3_lvds_dp_attach(struct drm_bridge *bridge)
> +{
> + struct ge_b850v3_lvds_dp *ptn_bridge
> + = bridge_to_ge_b850v3_lvds_dp(bridge);
> + struct drm_connector *connector = &ptn_bridge->connector;
> + struct i2c_client *ge_b850v3_lvds_dp_i2c
> + = ptn_bridge->ge_b850v3_lvds_dp_i2c;
> + int ret;
> +
> + if (!bridge->encoder) {
> + DRM_ERROR("Parent encoder object not found");
> + return -ENODEV;
> + }
> +
> + connector->polled = DRM_CONNECTOR_POLL_HPD;
> +
> + drm_connector_helper_add(connector,
> + &ge_b850v3_lvds_dp_connector_helper_funcs);
> +
> + ret = drm_connector_init(bridge->dev, connector,
> + &ge_b850v3_lvds_dp_connector_funcs,
> + DRM_MODE_CONNECTOR_DisplayPort);
> + if (ret) {
> + DRM_ERROR("Failed to initialize connector with drm\n");
> + return ret;
> + }
> +
> + ret = drm_mode_connector_attach_encoder(connector, bridge->encoder);
> + if (ret)
> + return ret;
> +
> + /* Configures the bridge to re-enable interrupts after each ack. */
> + i2c_smbus_write_word_data(ge_b850v3_lvds_dp_i2c,
> + STDP4028_IRQ_OUT_CONF_REG, STDP4028_DPTX_DP_IRQ_EN);
> +
> + /* Enable interrupts */
> + i2c_smbus_write_word_data(ge_b850v3_lvds_dp_i2c,
> + STDP4028_DPTX_IRQ_EN_REG, STDP4028_DPTX_IRQ_CONFIG);
> +
> + return 0;
> +}
> +
> +static const struct drm_bridge_funcs ge_b850v3_lvds_dp_funcs = {
> + .attach = ge_b850v3_lvds_dp_attach,
What about .detach? Should the interrupt be disabled?
> +};
> +
> +static int ge_b850v3_lvds_dp_probe(struct i2c_client *ge_b850v3_lvds_dp_i2c,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &ge_b850v3_lvds_dp_i2c->dev;
> + struct ge_b850v3_lvds_dp *ptn_bridge;
> + int ret;
> + u32 edid_i2c_reg;
> +
> + ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL);
> + if (!ptn_bridge)
> + return -ENOMEM;
> +
> + mutex_init(&ptn_bridge->lock);
> +
> + ptn_bridge->ge_b850v3_lvds_dp_i2c = ge_b850v3_lvds_dp_i2c;
> + ptn_bridge->bridge.driver_private = ptn_bridge;
> + i2c_set_clientdata(ge_b850v3_lvds_dp_i2c, ptn_bridge);
> +
> + ret = of_property_read_u32(dev->of_node, "edid-reg", &edid_i2c_reg);
> + if (ret) {
> + dev_err(dev, "edid-reg not specified, aborting...\n");
> + return -ENODEV;
> + }
> +
> + ptn_bridge->edid_i2c = devm_kzalloc(dev,
> + sizeof(struct i2c_client), GFP_KERNEL);
> +
Superfluous empty line, but ...
> + if (!ptn_bridge->edid_i2c)
> + return -ENOMEM;
> +
> + memcpy(ptn_bridge->edid_i2c, ge_b850v3_lvds_dp_i2c,
> + sizeof(struct i2c_client));
> +
> + ptn_bridge->edid_i2c->addr = (unsigned short) edid_i2c_reg;
... this looks to me like you should use i2c_new_dummy, or rather
i2c_new_secondary_device instead. For the latter you'd have to change
the bindings to use reg and reg-names properties instead of the custom
edid-reg property.
> +
> + ptn_bridge->bridge.funcs = &ge_b850v3_lvds_dp_funcs;
> + ptn_bridge->bridge.of_node = dev->of_node;
> + ret = drm_bridge_add(&ptn_bridge->bridge);
> + if (ret) {
> + DRM_ERROR("Failed to add bridge\n");
> + return ret;
> + }
Just as a note, this currently can not fail. It's just a list_add_tail
under a mutex lock.
If it would fail though, it'd be nice to also print the error code.
> +
> + /* Clear pending interrupts since power up. */
> + i2c_smbus_write_word_data(ge_b850v3_lvds_dp_i2c,
> + STDP4028_DPTX_IRQ_STS_REG, STDP4028_DPTX_IRQ_CLEAR);
> +
> + if (ge_b850v3_lvds_dp_i2c->irq) {
> + ret = devm_request_threaded_irq(&ge_b850v3_lvds_dp_i2c->dev,
> + ge_b850v3_lvds_dp_i2c->irq, NULL,
> + ge_b850v3_lvds_dp_irq_handler,
> + IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
> + "ge-b850v3-lvds-dp", ptn_bridge);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
Maybe a matter of taste, but this could be changed to:
/* If no interrupt has to be requested, we're done here */
if (!ge_b850v3_lvds_dp_i2c->irq)
return 0;
return devm_request_threaded_irq(&ge_b850v3_lvds_dp_i2c->dev,
ge_b850v3_lvds_dp_i2c->irq, NULL,
ge_b850v3_lvds_dp_irq_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ge-b850v3-lvds-dp", ptn_bridge);
or, as we know ret to be 0 after the call to drm_bridge_add:
if (ge_b850v3_lvds_dp_i2c->irq) {
ret = devm_request_threaded_irq(&ge_b850v3_lvds_dp_i2c->dev,
ge_b850v3_lvds_dp_i2c->irq, NULL,
ge_b850v3_lvds_dp_irq_handler,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"ge-b850v3-lvds-dp", ptn_bridge);
}
return ret;
> +}
> +
> +static int ge_b850v3_lvds_dp_remove(struct i2c_client *ge_b850v3_lvds_dp_i2c)
> +{
> + struct ge_b850v3_lvds_dp *ptn_bridge =
> + i2c_get_clientdata(ge_b850v3_lvds_dp_i2c);
> +
> + drm_bridge_remove(&ptn_bridge->bridge);
> +
> + kfree(ptn_bridge->edid);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id ge_b850v3_lvds_dp_i2c_table[] = {
> + {"b850v3-lvds-dp", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, ge_b850v3_lvds_dp_i2c_table);
> +
> +static const struct of_device_id ge_b850v3_lvds_dp_match[] = {
> + { .compatible = "ge,b850v3-lvds-dp" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, ge_b850v3_lvds_dp_match);
> +
> +static struct i2c_driver ge_b850v3_lvds_dp_driver = {
> + .id_table = ge_b850v3_lvds_dp_i2c_table,
> + .probe = ge_b850v3_lvds_dp_probe,
> + .remove = ge_b850v3_lvds_dp_remove,
> + .driver = {
> + .name = "b850v3-lvds-dp",
> + .of_match_table = ge_b850v3_lvds_dp_match,
> + },
> +};
> +module_i2c_driver(ge_b850v3_lvds_dp_driver);
> +
> +MODULE_AUTHOR("Peter Senna Tschudin <peter.senna@collabora.com>");
> +MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.co.uk>");
> +MODULE_DESCRIPTION("GE LVDS to DP++ display bridge)");
> +MODULE_LICENSE("GPL v2");
regards
Philipp
^ permalink raw reply
* Use of GICv3/ITS with PCIe host-generic driver - resizing ITS MAPD?
From: Alan Douglas @ 2016-11-01 11:47 UTC (permalink / raw)
To: linux-arm-kernel
I am using a Cadence PCIe Root Port in an ECAM setup with A53 and GICv3 with ITS, and have configured the host-generic driver to use MSI, using Device Tree.
Setup works well and the bus is correctly enumerated. However, to get MSI/MSI-X working correctly I needed to make a change in drivers/irqchip/irq-gic-v3-its.c
The PCI setup I am currently testing is:
# lspci -t
-[0000:00]---00.0-[01-04]----00.0-[02-04]--+-03.0-[03]----00.0
\-07.0-[04]----00.0
Device 00:00.0 is a pci-bridge, and claims 1 MSI interrupt.
01:00.0 is a pci-bridge, 1 MSI interrupt
02:03.0 is a pci-bridge, 1 MSI interrupt
02:03.0 is a pci-bridge, 1 MSI interrupt
03:00.0 is a USB controller with 2 MSI-X interrupts
04:00.0 is a SATA controller with 1 MSI interrupt
When setting up bus 0, the ITS device is created, and its_build_map_cmd() sets the size of the ITS MAPD based on the number of interrupts claimed by bus 0. When subsequent buses are enumerated, the ITS device will be reused, however we do not increase the number of supported interrupts to allow for the additional interrupts claimed by the additional devices being enumerated. (This can be seen in its_msi_prepare(), which is called for each device which has MSI/MSI-X enabled, and will reuse an existing ITS. )
The solution I have implemented is in its_alloc_device_irq(), if the offset into the LPI table for the allocated interrupt is greater than the ITS MAPD, I reallocate the itt area, and resize the ITS MAPD. I'm looking for comments as to whether this is a suitable solution and I should submit as a patch, is there some other recommendation or am I missing something regarding reuse of the ITS?
Regards
Alan Douglas
^ permalink raw reply
* [PATCH v2 0/3] Add Mediatek CIRQ interrupt controller
From: Youlin Pei @ 2016-11-01 11:51 UTC (permalink / raw)
To: linux-arm-kernel
In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to
works outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC.
The CIRQ controller is integrated in between MCUSYS and interrupt sources
as the second level interrupt controller. The external interrupts which
outside MCUSYS will feed through CIRQ then bypass to GIC.
In normal mode(where MCUSYS is active), CIRQ is disabled and interrupts
will directly issue to MCUSYS. When MCUSYS enters sleep mode, where GIC
is power downed. CIRQ will be enabled and monitor all edge trigger
interrupts(only edge trigger interrupts will be lost in this scenario).
When an edge interrupt is triggered, CIRQ will record the status and
generated a pulse signal to GIC when flush command is executed.
With CIRQ, MCUSYS can be completely turned off to improve the system
power consumption without losing interrupts.
change in v2:
1. fix coding style issue.
2. change the compatible string.
3. resolve IRQ offset at alloc time.
4. clear irq status in irq_eoi function.
5. rebase on 4.9-rc1.
v1:
http://lists.infradead.org/pipermail/linux-mediatek/2016-October/007213.html
Youlin Pei (3):
binding: irqchip: mtk-cirq: Add binding document
irqchip: mtk-cirq: Add mediatek mtk-cirq implement
ARM: dts: mt2701: Add mtk-cirq node for mt2701
.../interrupt-controller/mediatek,cirq.txt | 30 +++
arch/arm/boot/dts/mt2701.dtsi | 11 +-
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-mtk-cirq.c | 262 +++++++++++++++++++++
4 files changed, 303 insertions(+), 2 deletions(-)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
create mode 100644 drivers/irqchip/irq-mtk-cirq.c
--
1.9.1
^ permalink raw reply
* [PATCH v2 1/3] binding: irqchip: mtk-cirq: Add binding document
From: Youlin Pei @ 2016-11-01 11:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478001122-8664-1-git-send-email-youlin.pei@mediatek.com>
This commit adds the device tree binding document for
the mediatek cirq.
Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
---
.../interrupt-controller/mediatek,cirq.txt | 30 ++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
new file mode 100644
index 0000000..84e8123
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,cirq.txt
@@ -0,0 +1,30 @@
+* Mediatek 27xx cirq
+
+In Mediatek SOCs, the CIRQ is a low power interrupt controller designed to
+works outside MCUSYS which comprises with Cortex-Ax cores,CCI and GIC.
+The external interrupts (outside MCUSYS) will feed through CIRQ and connect
+to GIC in MCUSYS. When CIRQ is enabled, it will record the edge-sensitive
+interrupts and generated a pulse signal to parent interrupt controller when
+flush command is executed. With CIRQ, MCUSYS can be completely turned off
+to improve the system power consumption without losing interrupts.
+
+Required properties:
+- compatible: should be: "mediatek,mtk-cirq".
+- interrupt-controller : Identifies the node as an interrupt controller.
+- #interrupt-cells : Use the same format as specified by GIC in arm,gic.txt.
+- interrupt-parent: phandle of irq parent for cirq. The parent must
+ use the same interrupt-cells format as GIC.
+- reg: Physical base address of the cirq registers and length of memory
+ mapped region.
+- mediatek,ext-irq-start: Identifies external irq start number in different
+ SOCs.
+
+Example:
+ cirq: interrupt-controller at 10204000 {
+ compatible = "mediatek,mtk-cirq";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&sysirq>;
+ reg = <0 0x10204000 0 0x4000>;
+ mediatek,ext-irq-start = <32>;
+ };
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-11-01 11:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478001122-8664-1-git-send-email-youlin.pei@mediatek.com>
In Mediatek SOCs, the CIRQ is a low power interrupt controller
designed to works outside MCUSYS which comprises with Cortex-Ax
cores,CCI and GIC.
The CIRQ controller is integrated in between MCUSYS( include
Cortex-Ax, CCI and GIC ) and interrupt sources as the second
level interrupt controller. The external interrupts which outside
MCUSYS will feed through CIRQ then bypass to GIC. CIRQ can monitors
all edge trigger interupts. When an edge interrupt is triggered,
CIRQ can record the status and generate a pulse signal to GIC when
flush command executed.
When system enters sleep mode, MCUSYS will be turned off to improve
power consumption, also GIC is power down. The edge trigger interrupts
will be lost in this scenario without CIRQ.
This commit provides the CIRQ irqchip implement.
Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
---
drivers/irqchip/Makefile | 2 +-
drivers/irqchip/irq-mtk-cirq.c | 262 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 263 insertions(+), 1 deletion(-)
create mode 100644 drivers/irqchip/irq-mtk-cirq.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index e4dbfc8..8f33580 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -60,7 +60,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
-obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
+obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o
obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
new file mode 100644
index 0000000..fc43ef3
--- /dev/null
+++ b/drivers/irqchip/irq-mtk-cirq.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Youlin.Pei <youlin.pei@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+
+#define CIRQ_ACK 0x40
+#define CIRQ_MASK_SET 0xc0
+#define CIRQ_MASK_CLR 0x100
+#define CIRQ_SENS_SET 0x180
+#define CIRQ_SENS_CLR 0x1c0
+#define CIRQ_POL_SET 0x240
+#define CIRQ_POL_CLR 0x280
+#define CIRQ_CONTROL 0x300
+
+#define CIRQ_EN 0x1
+#define CIRQ_EDGE 0x2
+#define CIRQ_FLUSH 0x4
+
+#define CIRQ_IRQ_NUM 0x200
+
+struct mtk_cirq_chip_data {
+ void __iomem *base;
+ unsigned int ext_irq_start;
+};
+
+static struct mtk_cirq_chip_data *cirq_data;
+
+static void mtk_cirq_write_mask(struct irq_data *data, unsigned int offset)
+{
+ struct mtk_cirq_chip_data *chip_data = data->chip_data;
+ unsigned int cirq_num = data->hwirq;
+ u32 mask = 1 << (cirq_num % 32);
+
+ writel(mask, chip_data->base + offset + (cirq_num / 32) * 4);
+}
+
+static void mtk_cirq_mask(struct irq_data *data)
+{
+ mtk_cirq_write_mask(data, CIRQ_MASK_SET);
+ irq_chip_mask_parent(data);
+}
+
+static void mtk_cirq_unmask(struct irq_data *data)
+{
+ mtk_cirq_write_mask(data, CIRQ_MASK_CLR);
+ irq_chip_unmask_parent(data);
+}
+
+static void mtk_cirq_eoi(struct irq_data *data)
+{
+ mtk_cirq_write_mask(data, CIRQ_ACK);
+ irq_chip_eoi_parent(data);
+}
+
+static int mtk_cirq_set_type(struct irq_data *data, unsigned int type)
+{
+ int ret;
+
+ switch (type & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ mtk_cirq_write_mask(data, CIRQ_POL_CLR);
+ mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ mtk_cirq_write_mask(data, CIRQ_POL_SET);
+ mtk_cirq_write_mask(data, CIRQ_SENS_CLR);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ mtk_cirq_write_mask(data, CIRQ_POL_CLR);
+ mtk_cirq_write_mask(data, CIRQ_SENS_SET);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ mtk_cirq_write_mask(data, CIRQ_POL_SET);
+ mtk_cirq_write_mask(data, CIRQ_SENS_SET);
+ break;
+ default:
+ break;
+ }
+
+ data = data->parent_data;
+ ret = data->chip->irq_set_type(data, type);
+ return ret;
+}
+
+static struct irq_chip mtk_cirq_chip = {
+ .name = "MT_CIRQ",
+ .irq_mask = mtk_cirq_mask,
+ .irq_unmask = mtk_cirq_unmask,
+ .irq_eoi = mtk_cirq_eoi,
+ .irq_set_type = mtk_cirq_set_type,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+#endif
+};
+
+static int mtk_cirq_domain_translate(struct irq_domain *d,
+ struct irq_fwspec *fwspec,
+ unsigned long *hwirq,
+ unsigned int *type)
+{
+ if (is_of_node(fwspec->fwnode)) {
+ if (fwspec->param_count != 3)
+ return -EINVAL;
+
+ /* No PPI should point to this domain */
+ if (fwspec->param[0] != 0)
+ return -EINVAL;
+
+ /* cirq support irq number check */
+ if (fwspec->param[1] < cirq_data->ext_irq_start)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[1] - cirq_data->ext_irq_start;
+ *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int mtk_cirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ int i;
+ irq_hw_number_t hwirq;
+ struct irq_fwspec *fwspec = arg;
+ struct irq_fwspec parent_fwspec = *fwspec;
+
+ if (fwspec->param_count != 3)
+ return -EINVAL;
+
+ /* cirq doesn't support PPI */
+ if (fwspec->param[0])
+ return -EINVAL;
+
+ if (fwspec->param[1] < cirq_data->ext_irq_start)
+ return -EINVAL;
+
+ hwirq = fwspec->param[1] - cirq_data->ext_irq_start;
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &mtk_cirq_chip,
+ domain->host_data);
+
+ parent_fwspec.fwnode = domain->parent->fwnode;
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+ &parent_fwspec);
+}
+
+static const struct irq_domain_ops cirq_domain_ops = {
+ .translate = mtk_cirq_domain_translate,
+ .alloc = mtk_cirq_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_cirq_suspend(void)
+{
+ u32 value;
+
+ /* set edge_only mode, record edge-triggerd interrupts */
+ /* enable cirq */
+ value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+ value |= (CIRQ_EDGE | CIRQ_EN);
+ writel(value, cirq_data->base + CIRQ_CONTROL);
+ return 0;
+}
+
+static void mtk_cirq_resume(void)
+{
+ u32 value;
+
+ /* flush recored interrupts, will send signals to parent controller */
+ value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+ writel(value | CIRQ_FLUSH, cirq_data->base + CIRQ_CONTROL);
+
+ /* disable cirq */
+ value = readl_relaxed(cirq_data->base + CIRQ_CONTROL);
+ value &= ~(CIRQ_EDGE | CIRQ_EN);
+ writel(value, cirq_data->base + CIRQ_CONTROL);
+}
+
+static struct syscore_ops mtk_cirq_syscore_ops = {
+ .suspend = mtk_cirq_suspend,
+ .resume = mtk_cirq_resume,
+};
+
+static void mtk_cirq_syscore_init(void)
+{
+ register_syscore_ops(&mtk_cirq_syscore_ops);
+}
+#else
+static inline void mtk_cirq_syscore_init(void) {}
+#endif
+
+static int __init mtk_cirq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *domain, *domain_parent;
+ int ret;
+
+ domain_parent = irq_find_host(parent);
+ if (!domain_parent) {
+ pr_err("mtk_cirq: interrupt-parent not found\n");
+ return -EINVAL;
+ }
+
+ cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
+ if (!cirq_data)
+ return -ENOMEM;
+
+ cirq_data->base = of_iomap(node, 0);
+ if (!cirq_data->base) {
+ pr_err("mtk_cirq: unable to map cirq register\n");
+ ret = -ENXIO;
+ goto out_free;
+ }
+
+ ret = of_property_read_u32(node, "mediatek,ext-irq-start",
+ &cirq_data->ext_irq_start);
+ if (ret)
+ goto out_unmap;
+
+ domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
+ &cirq_domain_ops, cirq_data);
+ if (!domain) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ mtk_cirq_syscore_init();
+
+ return 0;
+
+out_unmap:
+ iounmap(cirq_data->base);
+out_free:
+ kfree(cirq_data);
+ return ret;
+}
+
+IRQCHIP_DECLARE(mtk_cirq, "mediatek,mtk-cirq", mtk_cirq_of_init);
--
1.7.9.5
^ permalink raw reply related
* [PATCH v2 3/3] ARM: dts: mt2701: Add mtk-cirq node for mt2701
From: Youlin Pei @ 2016-11-01 11:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478001122-8664-1-git-send-email-youlin.pei@mediatek.com>
This commit add mtk-cirq node to mt2701 dtsi.
Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
---
arch/arm/boot/dts/mt2701.dtsi | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 2800231..cd00bc4 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -22,7 +22,7 @@
/ {
compatible = "mediatek,mt2701";
- interrupt-parent = <&sysirq>;
+ interrupt-parent = <&cirq>;
cpus {
#address-cells = <1>;
@@ -170,6 +170,15 @@
reg = <0 0x10200100 0 0x1c>;
};
+ cirq: interrupt-controller at 10204000 {
+ compatible = "mediatek,mtk-cirq";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ interrupt-parent = <&sysirq>;
+ reg = <0 0x10204000 0 0x4000>;
+ mediatek,ext-irq-start = <32>;
+ };
+
apmixedsys: syscon at 10209000 {
compatible = "mediatek,mt2701-apmixedsys", "syscon";
reg = <0 0x10209000 0 0x1000>;
--
1.7.9.5
^ permalink raw reply related
* [SPAM][PATCH 2/3] irqchip: mtk-cirq: Add mediatek mtk-cirq implement
From: Youlin Pei @ 2016-11-01 12:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477580545.5951.3.camel@mtksdaap41>
On Thu, 2016-10-27 at 23:02 +0800, Yingjoe Chen wrote:
> On Thu, 2016-10-13 at 13:06 +0800, Youlin Pei wrote:
> > This commit add the mtk-cirq implement for mt2701.
> >
> > Signed-off-by: Youlin Pei <youlin.pei@mediatek.com>
> > ---
> > drivers/irqchip/Makefile | 2 +-
> > drivers/irqchip/irq-mtk-cirq.c | 257 ++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 258 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/irqchip/irq-mtk-cirq.c
> >
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index 4c203b6..eee95c6 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -59,7 +59,7 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
> > obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
> > obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
> > obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o
> > -obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o
> > +obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o irq-mtk-cirq.o
> > obj-$(CONFIG_ARCH_DIGICOLOR) += irq-digicolor.o
> > obj-$(CONFIG_RENESAS_H8300H_INTC) += irq-renesas-h8300h.o
> > obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o
> > diff --git a/drivers/irqchip/irq-mtk-cirq.c b/drivers/irqchip/irq-mtk-cirq.c
> > new file mode 100644
> > index 0000000..544767d
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-mtk-cirq.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Youlin.Pei <youlin.pei@mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/irq.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/of.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_address.h>
> > +#include <linux/io.h>
> > +#include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/syscore_ops.h>
> > +
> > +#define CIRQ_MASK_SET 0xC0
> > +#define CIRQ_MASK_CLR 0x100
> > +#define CIRQ_SENS_SET 0x180
> > +#define CIRQ_SENS_CLR 0x1C0
>
> nit: please use lower case for hex value
fixed in V2, please review in v2.
>
> > +#define CIRQ_POL_SET 0x240
> > +#define CIRQ_POL_CLR 0x280
> > +#define CIRQ_CONTROL 0x300
> > +
> > +#define CIRQ_EN 0x1
>
> nit: please align
fixed in v2. please review in v2.
>
> > +#define CIRQ_EDGE 0x2
> > +#define CIRQ_FLUSH 0x4
> > +
> > +#define CIRQ_IRQ_NUM 0x200
> > +
> > +struct mtk_cirq_chip_data {
> > + void __iomem *base;
> > + unsigned int ext_irq_start;
> > +};
> > +
>
> <deleted..>
>
> > +
> > +static int __init mtk_cirq_of_init(struct device_node *node,
> > + struct device_node *parent)
> > +{
> > + struct irq_domain *domain, *domain_parent;
> > + int ret;
> > +
> > + domain_parent = irq_find_host(parent);
> > + if (!domain_parent) {
> > + pr_err("mtk_cirq: interrupt-parent not found\n");
> > + return -EINVAL;
> > + }
> > +
> > + cirq_data = kzalloc(sizeof(*cirq_data), GFP_KERNEL);
> > + if (!cirq_data)
> > + return -ENOMEM;
> > +
> > + cirq_data->base = of_iomap(node, 0);
> > + if (!cirq_data->base) {
> > + pr_err("mtk_cirq: unable to map cirq register\n");
> > + ret = -ENXIO;
> > + goto out_free;
> > + }
> > +
> > + if (of_property_read_u32(node, "mediatek,ext-irq-start",
> > + &cirq_data->ext_irq_start)) {
> > + ret = -EINVAL;
> > + goto out_free;
>
> Please propagate error returned from of_property_read_u32
> Should goto out_unmap when fail here.
fixed in v2, thanks!
>
> Joe.C
>
> > + }
> > +
> > + domain = irq_domain_add_hierarchy(domain_parent, 0, CIRQ_IRQ_NUM, node,
> > + &cirq_domain_ops, cirq_data);
> > + if (!domain) {
> > + ret = -ENOMEM;
> > + goto out_unmap;
> > + }
> > +
> > + mtk_cirq_syscore_init();
> > +
> > + return 0;
> > +
> > +out_unmap:
> > + iounmap(cirq_data->base);
> > +out_free:
> > + kfree(cirq_data);
> > + return ret;
> > +}
> > +
> > +IRQCHIP_DECLARE(mtk_cirq, "mediatek,mt2701-cirq", mtk_cirq_of_init);
>
>
^ permalink raw reply
* [PATCH v26 0/7] arm64: add kdump support
From: Ruslan Bilovol @ 2016-11-01 12:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161018062617.GN19531@linaro.org>
On 10/18/2016 09:26 AM, AKASHI Takahiro wrote:
> Ruslan,
>
> On Mon, Oct 17, 2016 at 06:41:01PM +0300, Ruslan Bilovol wrote:
>> Hi,
>>
>> On 09/07/2016 07:29 AM, AKASHI Takahiro wrote:
>>> v26-specific note: After a comment from Rob[0], an idea of adding
>>> "linux,usable-memory-range" was dropped. Instead, an existing
>>> "reserved-memory" node will be used to limit usable memory ranges
>>> on crash dump kernel.
>>> This works not only on UEFI/ACPI systems but also on DT-only systems,
>>> but if he really insists on using DT-specific "usable-memory" property,
>>> I will post additional patches for kexec-tools. Those would be
>>> redundant, though.
>>> Even in that case, the kernel will not have to be changed.
>>>
>>> This patch series adds kdump support on arm64.
>>> There are some prerequisite patches [1],[2].
>>>
>>> To load a crash-dump kernel to the systems, a series of patches to
>>> kexec-tools, which have not yet been merged upstream, are needed.
>>> Please always use my latest kdump patches, v3 [3].
>>>
>>> To examine vmcore (/proc/vmcore) on a crash-dump kernel, you can use
>>> - crash utility (coming v7.1.6 or later) [4]
>>> (Necessary patches have already been queued in the master.)
>>>
>>>
>>> [0] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/452582.html
>>> [1] "arm64: mark reserved memblock regions explicitly in iomem"
>>> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
>>> [2] "efi: arm64: treat regions with WT/WC set but WB cleared as memory"
>>> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/451491.html
>>> [3] T.B.D.
>>> [4] https://github.com/crash-utility/crash.git
>> Are you going to rebase your patch series onto v4.9-rc1 tag soon? I see
> Yes, definitely as soon as possible! (actually I've done it.)
> But before submitting a new version, I need to convince Rob (Herring)
> that he would accept my old approach (v25) regarding specifying usable
> memory for crash dump kernel:
> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-September/459379.html
It looks like the patches got stuck on review.
Could you please share that rebased version of kernel and
kexec-tools (maybe even on Linaro private git repo), I'd like
to try it on our HW while review is in progress.
Thanks,
Ruslan
^ permalink raw reply
* [PATCH 2/3] ARM: imx: mach-imx6ul: add imx6ull support
From: Fabio Estevam @ 2016-11-01 12:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <HE1PR04MB1450456D09E01C3F308D25AB8BA10@HE1PR04MB1450.eurprd04.prod.outlook.com>
On Tue, Nov 1, 2016 at 8:44 AM, Peter Chen <peter.chen@nxp.com> wrote:
> This change does not be related with imx6ull support, but also a needed change since
> the formal name is capital letter "L", if you are concerned with that, I can delete this
> change.
Just make it as a separate patch then. Thanks
^ permalink raw reply
* [PATCH] staging: vc04_services: setup DMA and coherent mask
From: Robin Murphy @ 2016-11-01 12:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477943593.30971.3.camel@crowfest.net>
Hi Michael,
On 31/10/16 19:53, Michael Zoran wrote:
> On Mon, 2016-10-31 at 11:40 -0700, Michael Zoran wrote:
>> On Mon, 2016-10-31 at 11:36 -0700, Eric Anholt wrote:
>>> Michael Zoran <mzoran@crowfest.net> writes:
>>>
>>>> Setting the DMA mask is optional on 32 bit but
>>>> is mandatory on 64 bit. Set the DMA mask and coherent
>>>> to force all DMA to be in the 32 bit address space.
>>>>
>>>> This is considered a "good practice" and most drivers
>>>> already do this.
>>>>
>>>> Signed-off-by: Michael Zoran <mzoran@crowfest.net>
>>>> ---
[...]
>>>> + /*
>>>> + * Setting the DMA mask is necessary in the 64 bit
>>>> environment.
>>>> + * It isn't necessary in a 32 bit environment but is
>>>> considered
>>>> + * a good practice.
>>>> + */
>>>> + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>>>
>>> I think a better comment here would be simply:
>>>
>>> /* VCHI messages between the CPU and firmware use 32-bit bus
>>> addresses. */
>>>
>>> explaining why the value is chosen (once you know that the 32 bit
>>> restriction exists, reporting it is obviously needed). I'm
>>> curious,
>>> though: what failed when you didn't set it?
>>>
>>
>> The comment is easy to change.
>>
>> I don't have the log available ATM, but if I remember the DMA API's
>> bugcheck the first time that are used.
>>
>> I think this was a policy decision or something because the
>> information
>> should be available in the dma-ranges.
>>
>> If it's important, I can setup a test again without the change and e-
>> mail the logs.
>>
>> If you look at the DWC2 driver you will see that it also sets this
>> mask.
>
> OK, I'm begging to understand this. It appears the architecture
> specific paths are very different.
>
> In arm the mask and coherent is set to DMA_BIT_MASK(32) in mm/dma-
> mapping.c the first time the dma APIs are used. On arm64, it appears
> this variable is uninitialized and will contain random crude.
>
> Like I said, I don't know if this is a policy decision or if it just
> slipped through the cracks.
[...]
> arch/arm64/mm/dma-mapping.c(Note no call to get_coherent_dma_mask)
>
> static void *__dma_alloc(struct device *dev, size_t size,
> dma_addr_t *dma_handle, gfp_t flags,
> unsigned long attrs)
> {
> struct page *page;
> void *ptr, *coherent_ptr;
> bool coherent = is_device_dma_coherent(dev);
> pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL, false);
>
> size = PAGE_ALIGN(size);
>
> if (!coherent && !gfpflags_allow_blocking(flags)) {
> struct page *page = NULL;
> void *addr = __alloc_from_pool(size, &page, flags);
>
> if (addr)
> *dma_handle = phys_to_dma(dev,
> page_to_phys(page));
>
> return addr;
> }
>
> ptr = __dma_alloc_coherent(dev, size, dma_handle, flags,
> attrs);
In the general case, the device's coherent DMA mask is checked inside
that helper function, and then again further on in the SWIOTLB code if
necessary. For the atomic pool case beforehand, the pool is already
allocated below 4GB, and on arm64 we don't really expect devices to have
DMA masks *smaller* than 32 bits.
Devices created from DT get 32-bit DMA masks by default, although the
dma-ranges property may override that - see of_dma_configure() - so if
you somehow have a device with an uninitialised mask then I can only
assume there's some platform driver shenanigans going on. Adding the
dma_set_mask() call to the driver is no bad thing, but the rationale in
the commit message is bogus.
Robin.
^ permalink raw reply
* [PATCH RESEND 1/3] clk: imx6: Mask mmdc_ch1 handshake for periph2_sel and mmdc_ch1_axi_podf
From: Shawn Guo @ 2016-11-01 12:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOMZO5BQHKv7dxJTq2C1qdvhhRRRW8bafaXoQW77AKLNTWggAg@mail.gmail.com>
On Wed, Oct 26, 2016 at 01:48:56PM -0200, Fabio Estevam wrote:
> Shawn,
>
> Are you collecting the imx clk patches in this cycle?
>
> I see no response from Stephen on this series from a long time.
>
> We missed 4.9, so hopefully this can land in 4.10.
Okay, I just applied the series to imx/clk branch, and will get it into
linux-next for wider testing. If no issue is reported, I will send it
to Stephen and Mike then.
Shawn
^ permalink raw reply
* [PATCHv2] PCI: QDF2432 32 bit config space accessors
From: cov at codeaurora.org @ 2016-11-01 13:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161031214833.GB14603@bhelgaas-glaptop.roam.corp.google.com>
Hi Bjorn,
On 2016-10-31 15:48, Bjorn Helgaas wrote:
> On Wed, Sep 21, 2016 at 06:38:05PM -0400, Christopher Covington wrote:
>> The Qualcomm Technologies QDF2432 SoC does not support accesses
>> smaller
>> than 32 bits to the PCI configuration space. Register the appropriate
>> quirk.
>>
>> Signed-off-by: Christopher Covington <cov@codeaurora.org>
>
> Hi Christopher,
>
> Can you rebase this against v4.9-rc1? It no longer applies to my tree.
I apologize for not being clearer. This patch depends on:
PCI/ACPI: Extend pci_mcfg_lookup() responsibilities
PCI/ACPI: Check platform-specific ECAM quirks
These patches from Tomasz Nowicki were previously in your pci/ecam-v6
branch, but that seems to have come and gone. How would you like to
proceed?
> Note that this hardware is not spec-compliant since it doesn't support
> sub-32 bit config writes. I just proposed a patch to warn about that
> [1], so if/when we merge that patch and this one, you'll start seeing
> those warnings.
>
> [1]
> http://lkml.kernel.org/r/20161031213902.6340.96123.stgit at bhelgaas-glaptop.roam.corp.google.com
That looks great, thank you. The earlier PCI HDL and SoC vendors can be
made aware of such problems, the better.
Thanks,
Cov
^ permalink raw reply
* [PATCH/RESEND V4 0/3] ARM64 LPC: legacy ISA I/O support
From: zhichang.yuan @ 2016-11-01 13:28 UTC (permalink / raw)
To: linux-arm-kernel
This patch supports the IPMI-bt device attached to the Low-Pin-Count interface
implemented on Hisilicon Hip06 SoC.
-----------
| LPC host|
| |
-----------
|
_____________V_______________LPC
| |
V V
------------
| BT(ipmi)|
------------
When master accesses those periperals beneath the Hip06 LPC, a specific LPC
driver is needed to make LPC host generate the standard LPC I/O cycles with
the target periperals'I/O port addresses. But on curent arm64 world, there is
no real I/O accesses. All the I/O operations through in/out pair are based on
MMIO which is not satisfied the I/O mechanism on Hip06 LPC.
To solve this issue and keep the relevant existing peripherals' driver
unchanged, this patch set redefines the in/out pair to support both the IO
operations for Hip06 LPC and the original MMIO. The way specific to Hip06 is
named as indirect-IO in this patchset.
Changes from V3:
- UART support deferred to a separate patchset; This patchset only support
ipmi device under LPC;
- LPC bus I/O range is fixed to 0 ~ (PCIBIOS_MIN_IO - 1), which is separeted
from PCI/PCIE PIO space;
- Based on Arnd's remarks, removed the ranges property from Hip06 lpc dts and
added a new fixup function, of_isa_indirect_io(), to get the I/O address
directly from LPC dts configurations;
- Support in(w,l)/out(w,l) for Hip06 lpc I/O;
- Decouple the header file dependency on the gerenic io.h by defining in/out
as normal functions in c file;
- removed unused macro definitions in the LPC driver;
Changes from V2:
- Support the PIO retrieval from the linux PIO generated by
pci_address_to_pio. This method replace the 4K PIO reservation in V2;
- Support the flat-tree earlycon;
- Some revises based on Arnd's remarks;
- Make sure the linux PIO range allocated to Hip06 LPC peripherals starts
from non-ZERO;
Changes from V1:
- Support the ACPI LPC device;
- Optimize the dts LPC driver in ISA compatible mode;
- Reserve the IO range below 4K in avoid the possible conflict with PCI host
IO ranges;
- Support the LPC uart and relevant earlycon;
Signed-off-by: Zhichang Yuan <yuanzhichang@hisilicon.com>
zhichang.yuan (3):
ARM64 LPC: Indirect ISA port IO introduced
ARM64 LPC: Add missing range exception for special ISA
ARM64 LPC: LPC driver implementation on Hip06
.../arm/hisilicon/hisilicon-low-pin-count.txt | 31 ++
MAINTAINERS | 8 +
arch/arm64/Kconfig | 6 +
arch/arm64/include/asm/extio.h | 94 ++++
arch/arm64/include/asm/io.h | 36 ++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/extio.c | 53 +++
drivers/bus/Kconfig | 8 +
drivers/bus/Makefile | 1 +
drivers/bus/hisi_lpc.c | 501 +++++++++++++++++++++
drivers/of/address.c | 47 +-
drivers/pci/pci.c | 6 +-
include/linux/of_address.h | 17 +
13 files changed, 804 insertions(+), 5 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
create mode 100644 arch/arm64/include/asm/extio.h
create mode 100644 arch/arm64/kernel/extio.c
create mode 100644 drivers/bus/hisi_lpc.c
--
1.9.1
^ permalink raw reply
* [PATCH/RESEND V4 1/3] ARM64 LPC: Indirect ISA port IO introduced
From: zhichang.yuan @ 2016-11-01 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478006926-240933-1-git-send-email-yuanzhichang@hisilicon.com>
For arm64, there is no I/O space as other architectural platforms, such as
X86. Most I/O accesses are achieved based on MMIO. But for some arm64 SoCs,
such as Hip06, when accessing some legacy ISA devices connected to LPC, those
known port addresses are used to control the corresponding target devices, for
example, 0x2f8 is for UART, 0xe4 is for ipmi-bt. It is different from the
normal MMIO mode in using.
To drive these devices, this patch introduces a method named indirect-IO.
In this method the in/out pair in arch/arm64/include/asm/io.h will be
redefined. When upper layer drivers call in/out with those known legacy port
addresses to access the peripherals, the hooking functions corrresponding to
those target peripherals will be called. Through this way, those upper layer
drivers which depend on in/out can run on Hip06 without any changes.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
arch/arm64/Kconfig | 6 +++
arch/arm64/include/asm/extio.h | 94 ++++++++++++++++++++++++++++++++++++++++++
arch/arm64/include/asm/io.h | 29 +++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/extio.c | 29 +++++++++++++
5 files changed, 159 insertions(+)
create mode 100644 arch/arm64/include/asm/extio.h
create mode 100644 arch/arm64/kernel/extio.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 30398db..103dbea 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -163,6 +163,12 @@ config ARCH_MMAP_RND_COMPAT_BITS_MIN
config ARCH_MMAP_RND_COMPAT_BITS_MAX
default 16
+config ARM64_INDIRECT_PIO
+ bool "access peripherals with legacy I/O port"
+ help
+ Support special accessors for ISA I/O devices. This is needed for
+ SoCs that do not support standard read/write for the ISA range.
+
config NO_IOPORT_MAP
def_bool y if !PCI
diff --git a/arch/arm64/include/asm/extio.h b/arch/arm64/include/asm/extio.h
new file mode 100644
index 0000000..6ae0787
--- /dev/null
+++ b/arch/arm64/include/asm/extio.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_EXTIO_H
+#define __LINUX_EXTIO_H
+
+struct extio_ops {
+ unsigned long start;/* inclusive, sys io addr */
+ unsigned long end;/* inclusive, sys io addr */
+
+ u64 (*pfin)(void *devobj, unsigned long ptaddr, size_t dlen);
+ void (*pfout)(void *devobj, unsigned long ptaddr, u32 outval,
+ size_t dlen);
+ u64 (*pfins)(void *devobj, unsigned long ptaddr, void *inbuf,
+ size_t dlen, unsigned int count);
+ void (*pfouts)(void *devobj, unsigned long ptaddr,
+ const void *outbuf, size_t dlen,
+ unsigned int count);
+ void *devpara;
+};
+
+extern struct extio_ops *arm64_extio_ops;
+
+#define DECLARE_EXTIO(bw, type) \
+extern type in##bw(unsigned long addr); \
+extern void out##bw(type value, unsigned long addr); \
+extern void ins##bw(unsigned long addr, void *buffer, unsigned int count);\
+extern void outs##bw(unsigned long addr, const void *buffer, unsigned int count);
+
+#define BUILD_EXTIO(bw, type) \
+type in##bw(unsigned long addr) \
+{ \
+ if (!arm64_extio_ops || arm64_extio_ops->start > addr || \
+ arm64_extio_ops->end < addr) \
+ return read##bw(PCI_IOBASE + addr); \
+ return arm64_extio_ops->pfin ? \
+ arm64_extio_ops->pfin(arm64_extio_ops->devpara, \
+ addr, sizeof(type)) : -1; \
+} \
+ \
+void out##bw(type value, unsigned long addr) \
+{ \
+ if (!arm64_extio_ops || arm64_extio_ops->start > addr || \
+ arm64_extio_ops->end < addr) \
+ write##bw(value, PCI_IOBASE + addr); \
+ else \
+ if (arm64_extio_ops->pfout) \
+ arm64_extio_ops->pfout(arm64_extio_ops->devpara,\
+ addr, value, sizeof(type)); \
+} \
+ \
+void ins##bw(unsigned long addr, void *buffer, unsigned int count) \
+{ \
+ if (!arm64_extio_ops || arm64_extio_ops->start > addr || \
+ arm64_extio_ops->end < addr) \
+ reads##bw(PCI_IOBASE + addr, buffer, count); \
+ else \
+ if (arm64_extio_ops->pfins) \
+ arm64_extio_ops->pfins(arm64_extio_ops->devpara,\
+ addr, buffer, sizeof(type), count); \
+} \
+ \
+void outs##bw(unsigned long addr, const void *buffer, unsigned int count) \
+{ \
+ if (!arm64_extio_ops || arm64_extio_ops->start > addr || \
+ arm64_extio_ops->end < addr) \
+ writes##bw(PCI_IOBASE + addr, buffer, count); \
+ else \
+ if (arm64_extio_ops->pfouts) \
+ arm64_extio_ops->pfouts(arm64_extio_ops->devpara,\
+ addr, buffer, sizeof(type), count); \
+}
+
+static inline void arm64_set_extops(struct extio_ops *ops)
+{
+ if (ops)
+ WRITE_ONCE(arm64_extio_ops, ops);
+}
+
+#endif /* __LINUX_EXTIO_H*/
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 0bba427..136735d 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -31,6 +31,7 @@
#include <asm/early_ioremap.h>
#include <asm/alternative.h>
#include <asm/cpufeature.h>
+#include <asm/extio.h>
#include <xen/xen.h>
@@ -149,6 +150,34 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
#define IO_SPACE_LIMIT (PCI_IO_SIZE - 1)
#define PCI_IOBASE ((void __iomem *)PCI_IO_START)
+
+/*
+ * redefine the in(s)b/out(s)b for indirect-IO.
+ */
+#ifdef CONFIG_ARM64_INDIRECT_PIO
+#define inb inb
+#define outb outb
+#define insb insb
+#define outsb outsb
+/* external declaration */
+DECLARE_EXTIO(b, u8)
+
+#define inw inw
+#define outw outw
+#define insw insw
+#define outsw outsw
+
+DECLARE_EXTIO(w, u16)
+
+#define inl inl
+#define outl outl
+#define insl insl
+#define outsl outsl
+
+DECLARE_EXTIO(l, u32)
+#endif
+
+
/*
* String version of I/O memory access operations.
*/
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 7d66bba..60e0482 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -31,6 +31,7 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o entry32.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
+arm64-obj-$(CONFIG_ARM64_INDIRECT_PIO) += extio.o
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
diff --git a/arch/arm64/kernel/extio.c b/arch/arm64/kernel/extio.c
new file mode 100644
index 0000000..80cafd5
--- /dev/null
+++ b/arch/arm64/kernel/extio.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+
+struct extio_ops *arm64_extio_ops;
+
+
+BUILD_EXTIO(b, u8)
+
+BUILD_EXTIO(w, u16)
+
+BUILD_EXTIO(l, u32)
+
+#endif
--
1.9.1
^ permalink raw reply related
* [PATCH/RESEND V4 2/3] ARM64 LPC: Add missing range exception for special ISA
From: zhichang.yuan @ 2016-11-01 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478006926-240933-1-git-send-email-yuanzhichang@hisilicon.com>
Currently if the range property is not specified of_translate_one
returns an error. There are some special devices that work on a
range of I/O ports where it's is not correct to specify a range
property as the cpu addresses are used by special accessors.
Here we add a new exception in of_translate_one to return
the cpu address if the range property is not there. The exception
checks if the parent bus is ISA and if the special accessors are
defined.
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Rob Herring <robh+dt@kernel.org>
Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
arch/arm64/include/asm/io.h | 7 +++++++
arch/arm64/kernel/extio.c | 24 +++++++++++++++++++++++
drivers/of/address.c | 47 +++++++++++++++++++++++++++++++++++++++++++--
drivers/pci/pci.c | 6 +++---
include/linux/of_address.h | 17 ++++++++++++++++
5 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index 136735d..e480199 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -175,6 +175,13 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
#define outsl outsl
DECLARE_EXTIO(l, u32)
+
+
+#define indirect_io_ison indirect_io_ison
+extern int indirect_io_ison(void);
+
+#define chk_indirect_range chk_indirect_range
+extern int chk_indirect_range(u64 taddr);
#endif
diff --git a/arch/arm64/kernel/extio.c b/arch/arm64/kernel/extio.c
index 80cafd5..55df8dc 100644
--- a/arch/arm64/kernel/extio.c
+++ b/arch/arm64/kernel/extio.c
@@ -19,6 +19,30 @@
struct extio_ops *arm64_extio_ops;
+/**
+ * indirect_io_ison - check whether indirectIO can work well. This function only call
+ * before the target I/O address was obtained.
+ *
+ * Returns 1 when indirectIO can work.
+ */
+int indirect_io_ison()
+{
+ return arm64_extio_ops ? 1 : 0;
+}
+
+/**
+ * check_indirect_io - check whether the input taddr is for indirectIO.
+ * @taddr: the io address to be checked.
+ *
+ * Returns 1 when taddr is in the range; otherwise return 0.
+ */
+int chk_indirect_range(u64 taddr)
+{
+ if (arm64_extio_ops->start > taddr || arm64_extio_ops->end < taddr)
+ return 0;
+
+ return 1;
+}
BUILD_EXTIO(b, u8)
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 02b2903..0bee822 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -479,6 +479,39 @@ static int of_empty_ranges_quirk(struct device_node *np)
return false;
}
+
+/*
+ * Check whether the current device being translating use indirectIO.
+ *
+ * return 1 if the check is past, or 0 represents fail checking.
+ */
+static int of_isa_indirect_io(struct device_node *parent,
+ struct of_bus *bus, __be32 *addr,
+ int na, u64 *presult)
+{
+ unsigned int flags;
+ unsigned int rlen;
+
+ /* whether support indirectIO */
+ if (!indirect_io_ison())
+ return 0;
+
+ if (!of_bus_isa_match(parent))
+ return 0;
+
+ flags = bus->get_flags(addr);
+ if (!(flags & IORESOURCE_IO))
+ return 0;
+
+ /* there is ranges property, apply the normal translation directly. */
+ if (of_get_property(parent, "ranges", &rlen))
+ return 0;
+
+ *presult = of_read_number(addr + 1, na - 1);
+
+ return chk_indirect_range(*presult);
+}
+
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
@@ -532,7 +565,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
}
memcpy(addr, ranges + na, 4 * pna);
- finish:
+finish:
of_dump_addr("parent translation for:", addr, pna);
pr_debug("with offset: %llx\n", (unsigned long long)offset);
@@ -595,6 +628,15 @@ static u64 __of_translate_address(struct device_node *dev,
result = of_read_number(addr, na);
break;
}
+ /*
+ * For indirectIO device which has no ranges property, get
+ * the address from reg directly.
+ */
+ if (of_isa_indirect_io(dev, bus, addr, na, &result)) {
+ pr_info("isa indirectIO matched(%s)..addr = 0x%llx\n",
+ of_node_full_name(dev), result);
+ break;
+ }
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
@@ -688,8 +730,9 @@ static int __of_address_to_resource(struct device_node *dev,
if (taddr == OF_BAD_ADDR)
return -EINVAL;
memset(r, 0, sizeof(struct resource));
- if (flags & IORESOURCE_IO) {
+ if (flags & IORESOURCE_IO && taddr >= PCIBIOS_MIN_IO) {
unsigned long port;
+
port = pci_address_to_pio(taddr);
if (port == (unsigned long)-1)
return -EINVAL;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ba34907..1a08511 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3263,7 +3263,7 @@ int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
#ifdef PCI_IOBASE
struct io_range *range;
- resource_size_t allocated_size = 0;
+ resource_size_t allocated_size = PCIBIOS_MIN_IO;
/* check if the range hasn't been previously recorded */
spin_lock(&io_range_lock);
@@ -3312,7 +3312,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
#ifdef PCI_IOBASE
struct io_range *range;
- resource_size_t allocated_size = 0;
+ resource_size_t allocated_size = PCIBIOS_MIN_IO;
if (pio > IO_SPACE_LIMIT)
return address;
@@ -3335,7 +3335,7 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
{
#ifdef PCI_IOBASE
struct io_range *res;
- resource_size_t offset = 0;
+ resource_size_t offset = PCIBIOS_MIN_IO;
unsigned long addr = -1;
spin_lock(&io_range_lock);
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 3786473..0ba7e21 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -24,6 +24,23 @@ struct of_pci_range {
#define for_each_of_pci_range(parser, range) \
for (; of_pci_range_parser_one(parser, range);)
+
+#ifndef indirect_io_ison
+#define indirect_io_ison indirect_io_ison
+static inline int indirect_io_ison(void)
+{
+ return 0;
+}
+#endif
+
+#ifndef chk_indirect_range
+#define chk_indirect_range chk_indirect_range
+static inline int chk_indirect_range(u64 taddr)
+{
+ return 0;
+}
+#endif
+
/* Translate a DMA address from device space to CPU space */
extern u64 of_translate_dma_address(struct device_node *dev,
const __be32 *in_addr);
--
1.9.1
^ permalink raw reply related
* [PATCH/RESEND V4 3/3] ARM64 LPC: LPC driver implementation on Hip06
From: zhichang.yuan @ 2016-11-01 13:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1478006926-240933-1-git-send-email-yuanzhichang@hisilicon.com>
On Hip06, the accesses to LPC peripherals work in an indirect way. A
corresponding LPC driver configure some registers in LPC master at first, then
the real accesses on LPC slave devices are finished by the LPC master, which
is transparent to LPC driver.
This patch implement the relevant driver for Hip06 LPC. Cooperating with
indirect-IO, ipmi messages is in service without any changes on ipmi driver.
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: zhichang.yuan <yuanzhichang@hisilicon.com>
Signed-off-by: Gabriele Paoloni <gabriele.paoloni@huawei.com>
---
.../arm/hisilicon/hisilicon-low-pin-count.txt | 31 ++
MAINTAINERS | 8 +
drivers/bus/Kconfig | 8 +
drivers/bus/Makefile | 1 +
drivers/bus/hisi_lpc.c | 501 +++++++++++++++++++++
5 files changed, 549 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
create mode 100644 drivers/bus/hisi_lpc.c
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
new file mode 100644
index 0000000..e681419
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
@@ -0,0 +1,31 @@
+Hisilicon Hip06 low-pin-count device
+ Usually LPC controller is part of PCI host bridge, so the legacy ISA ports
+ locate on LPC bus can be accessed direclty. But some SoCs have independent
+ LPC controller, and access the legacy ports by triggering LPC I/O cycles.
+ Hisilicon Hip06 implements this LPC device.
+
+Required properties:
+- compatible: should be "hisilicon,low-pin-count"
+- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
+- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
+- reg: base memory range where the register set for this device is mapped.
+
+Note:
+ The node name before '@' must be "isa" to represent the binding stick to the
+ ISA/EISA binding specification.
+
+Example:
+
+isa at a01b0000 {
+ compatible = "hisilicom,low-pin-count";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ reg = <0x0 0xa01b0000 0x0 0x1000>;
+
+ ipmi0: bt at e4 {
+ compatible = "ipmi-bt";
+ device_type = "ipmi";
+ reg = <0x01 0xe4 0x04>;
+ status = "disabled";
+ };
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 1cd38a7..7c69410 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5716,6 +5716,14 @@ F: include/uapi/linux/if_hippi.h
F: net/802/hippi.c
F: drivers/net/hippi/
+HISILICON LPC BUS DRIVER
+M: Zhichang Yuan <yuanzhichang@hisilicon.com>
+L: linux-arm-kernel at lists.infradead.org
+W: http://www.hisilicon.com
+S: Maintained
+F: drivers/bus/hisi_lpc.c
+F: Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
+
HISILICON NETWORK SUBSYSTEM DRIVER
M: Yisen Zhuang <yisen.zhuang@huawei.com>
M: Salil Mehta <salil.mehta@huawei.com>
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 7010dca..a108abc 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -64,6 +64,14 @@ config BRCMSTB_GISB_ARB
arbiter. This driver provides timeout and target abort error handling
and internal bus master decoding.
+config HISILICON_LPC
+ bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X"
+ depends on (ARCH_HISI || COMPILE_TEST) && ARM64
+ select ARM64_INDIRECT_PIO
+ help
+ Driver needed for some legacy ISA devices attached to Low-Pin-Count
+ on Hisilicon Hip0X SoC.
+
config IMX_WEIM
bool "Freescale EIM DRIVER"
depends on ARCH_MXC
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index c6cfa6b..10b4983 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
+obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
new file mode 100644
index 0000000..9f48a1a
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2016 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/serial_8250.h>
+#include <linux/slab.h>
+
+/*
+ * this bit set means each IO operation will target to different port address;
+ * 0 means repeatly IO operations will be sticked on the same port, such as BT;
+ */
+#define FG_INCRADDR_LPC 0x02
+
+struct lpc_cycle_para {
+ unsigned int opflags;
+ unsigned int csize;/* the data length of each operation */
+};
+
+struct hisilpc_dev {
+ spinlock_t cycle_lock;
+ void __iomem *membase;
+ struct extio_ops io_ops;
+};
+
+
+/* The maximum continous operations*/
+#define LPC_MAX_OPCNT 16
+/* only support IO data unit length is four at maximum */
+#define LPC_MAX_DULEN 4
+#if LPC_MAX_DULEN > LPC_MAX_OPCNT
+#error "LPC.. MAX_DULEN must be not bigger than MAX_OPCNT!"
+#endif
+
+#define LPC_REG_START 0x00/* start a new LPC cycle */
+#define LPC_REG_OP_STATUS 0x04/* the current LPC status */
+#define LPC_REG_IRQ_ST 0x08/* interrupt enable&status */
+#define LPC_REG_OP_LEN 0x10/* how many LPC cycles each start */
+#define LPC_REG_CMD 0x14/* command for the required LPC cycle */
+#define LPC_REG_ADDR 0x20/* LPC target address */
+#define LPC_REG_WDATA 0x24/* data to be written */
+#define LPC_REG_RDATA 0x28/* data coming from peer */
+
+
+/* The command register fields*/
+#define LPC_CMD_SAMEADDR 0x08
+#define LPC_CMD_TYPE_IO 0x00
+#define LPC_CMD_WRITE 0x01
+#define LPC_CMD_READ 0x00
+/* the bit attribute is W1C. 1 represents OK. */
+#define LPC_STAT_BYIRQ 0x02
+
+#define LPC_STATUS_IDLE 0x01
+#define LPC_OP_FINISHED 0x02
+
+#define START_WORK 0x01
+
+/*
+ * The minimal waiting interval... Suggest it is not less than 10.
+ * Bigger value probably will lower the performance.
+ */
+#define LPC_NSEC_PERWAIT 100
+/*
+ * The maximum waiting time is about 128us.
+ * The fastest IO cycle time is about 390ns, but the worst case will wait
+ * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum
+ * burst cycles is 16. So, the maximum waiting time is about 128us under
+ * worst case.
+ * choose 1300 as the maximum.
+ */
+#define LPC_MAX_WAITCNT 1300
+/* About 10us. This is specfic for single IO operation, such as inb. */
+#define LPC_PEROP_WAITCNT 100
+
+
+static inline int wait_lpc_idle(unsigned char *mbase,
+ unsigned int waitcnt) {
+ u32 opstatus;
+
+ while (waitcnt--) {
+ ndelay(LPC_NSEC_PERWAIT);
+ opstatus = readl(mbase + LPC_REG_OP_STATUS);
+ if (opstatus & LPC_STATUS_IDLE)
+ return (opstatus & LPC_OP_FINISHED) ? 0 : (-EIO);
+ }
+ return -ETIME;
+}
+
+
+/**
+ * hisilpc_target_in - trigger a series of lpc cycles to read required data
+ * from target periperal.
+ * @pdev: pointer to hisi lpc device
+ * @para: some paramerters used to control the lpc I/O operations
+ * @ptaddr: the lpc I/O target port address
+ * @buf: where the read back data is stored
+ * @opcnt: how many I/O operations required in this calling
+ *
+ * only one byte data is read each I/O operation.
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int hisilpc_target_in(struct hisilpc_dev *pdev,
+ struct lpc_cycle_para *para,
+ unsigned long ptaddr, unsigned char *buf,
+ unsigned long opcnt)
+{
+ unsigned int cmd_word;
+ unsigned int waitcnt;
+ int retval;
+ unsigned long flags;
+ unsigned long cnt_per_trans;
+
+ if (!buf || !opcnt || !para || !para->csize || !pdev)
+ return -EINVAL;
+
+ if (opcnt > LPC_MAX_OPCNT)
+ return -EINVAL;
+
+ cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_READ;
+ waitcnt = (LPC_PEROP_WAITCNT);
+ if (!(para->opflags & FG_INCRADDR_LPC)) {
+ cmd_word |= LPC_CMD_SAMEADDR;
+ waitcnt = LPC_MAX_WAITCNT;
+ }
+
+ retval = 0;
+ cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
+ for (; opcnt && !retval; cnt_per_trans = para->csize) {
+ /* whole operation must be atomic */
+ spin_lock_irqsave(&pdev->cycle_lock, flags);
+
+ writel(cnt_per_trans, pdev->membase + LPC_REG_OP_LEN);
+
+ writel(cmd_word, pdev->membase + LPC_REG_CMD);
+
+ writel(ptaddr, pdev->membase + LPC_REG_ADDR);
+
+ writel(START_WORK, pdev->membase + LPC_REG_START);
+
+ /* whether the operation is finished */
+ retval = wait_lpc_idle(pdev->membase, waitcnt);
+ if (!retval) {
+ opcnt -= cnt_per_trans;
+ for (; cnt_per_trans--; buf++)
+ *buf = readl(pdev->membase + LPC_REG_RDATA);
+ }
+
+ spin_unlock_irqrestore(&pdev->cycle_lock, flags);
+ }
+
+ return retval;
+}
+
+/**
+ * hisilpc_target_out - trigger a series of lpc cycles to write required data
+ * to target periperal.
+ * @pdev: pointer to hisi lpc device
+ * @para: some paramerters used to control the lpc I/O operations
+ * @ptaddr: the lpc I/O target port address
+ * @buf: where the data to be written is stored
+ * @opcnt: how many I/O operations required
+ *
+ * only one byte data is read each I/O operation.
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int hisilpc_target_out(struct hisilpc_dev *pdev,
+ struct lpc_cycle_para *para,
+ unsigned long ptaddr,
+ const unsigned char *buf,
+ unsigned long opcnt)
+{
+ unsigned int cmd_word;
+ unsigned int waitcnt;
+ int retval;
+ unsigned long flags;
+ unsigned long cnt_per_trans;
+
+ if (!buf || !opcnt || !para || !pdev)
+ return -EINVAL;
+
+ if (opcnt > LPC_MAX_OPCNT)
+ return -EINVAL;
+ /* default is increasing address */
+ cmd_word = LPC_CMD_TYPE_IO | LPC_CMD_WRITE;
+ waitcnt = (LPC_PEROP_WAITCNT);
+ if (!(para->opflags & FG_INCRADDR_LPC)) {
+ cmd_word |= LPC_CMD_SAMEADDR;
+ waitcnt = LPC_MAX_WAITCNT;
+ }
+
+ retval = 0;
+ cnt_per_trans = (para->csize == 1) ? opcnt : para->csize;
+ for (; opcnt && !retval; cnt_per_trans = para->csize) {
+ spin_lock_irqsave(&pdev->cycle_lock, flags);
+
+ writel(cnt_per_trans, pdev->membase + LPC_REG_OP_LEN);
+ opcnt -= cnt_per_trans;
+ for (; cnt_per_trans--; buf++)
+ writel(*buf, pdev->membase + LPC_REG_WDATA);
+
+ writel(cmd_word, pdev->membase + LPC_REG_CMD);
+
+ writel(ptaddr, pdev->membase + LPC_REG_ADDR);
+
+ writel(START_WORK, pdev->membase + LPC_REG_START);
+
+ /* whether the operation is finished */
+ retval = wait_lpc_idle(pdev->membase, waitcnt);
+
+ spin_unlock_irqrestore(&pdev->cycle_lock, flags);
+ }
+
+ return retval;
+}
+
+
+/**
+ * hisilpc_comm_in - read/input the data from the I/O peripheral through LPC.
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @ptaddr: the target I/O port address.
+ * @dlen: the data length required to read from the target I/O port.
+ *
+ * when succeed, the data read back is stored in buffer pointed by inbuf.
+ * For inb, return the data read from I/O or -1 when error occur.
+ */
+static u64 hisilpc_comm_in(void *devobj, unsigned long ptaddr, size_t dlen)
+{
+ struct hisilpc_dev *lpcdev;
+ struct lpc_cycle_para iopara;
+ u32 rd_data;
+ unsigned char *newbuf;
+ int ret = 0;
+
+ if (!devobj || !dlen || dlen > LPC_MAX_DULEN || (dlen & (dlen - 1)))
+ return -1;
+
+ /* the local buffer must be enough for one data unit */
+ if (sizeof(rd_data) < dlen)
+ return -1;
+
+ newbuf = (unsigned char *)&rd_data;
+
+ lpcdev = (struct hisilpc_dev *)devobj;
+
+ iopara.opflags = FG_INCRADDR_LPC;
+ iopara.csize = dlen;
+
+ ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, newbuf, dlen);
+ if (ret)
+ return -1;
+
+ return le32_to_cpu(rd_data);
+}
+
+/**
+ * hisilpc_comm_out - write/output the data whose maximal length is four bytes to
+ * the I/O peripheral through LPC.
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @outval: a value to be outputed from caller, maximum is four bytes.
+ * @ptaddr: the target I/O port address.
+ * @dlen: the data length required writing to the target I/O port .
+ *
+ * This function is corresponding to out(b,w,l) only
+ *
+ */
+static void hisilpc_comm_out(void *devobj, unsigned long ptaddr,
+ u32 outval, size_t dlen)
+{
+ struct hisilpc_dev *lpcdev;
+ struct lpc_cycle_para iopara;
+ const unsigned char *newbuf;
+
+ if (!devobj || !dlen || dlen > LPC_MAX_DULEN)
+ return;
+
+ if (sizeof(outval) < dlen)
+ return;
+
+ outval = cpu_to_le32(outval);
+
+ newbuf = (const unsigned char *)&outval;
+ lpcdev = (struct hisilpc_dev *)devobj;
+
+ iopara.opflags = FG_INCRADDR_LPC;
+ iopara.csize = dlen;
+
+ hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf, dlen);
+}
+
+
+/**
+ * hisilpc_comm_ins - read/input the data in buffer to the I/O peripheral
+ * through LPC, it corresponds to ins(b,w,l)
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @ptaddr: the target I/O port address.
+ * @inbuf: a buffer where read/input data bytes are stored.
+ * @dlen: the data length required writing to the target I/O port .
+ * @count: how many data units whose length is dlen will be read.
+ *
+ */
+static u64 hisilpc_comm_ins(void *devobj, unsigned long ptaddr,
+ void *inbuf, size_t dlen, unsigned int count)
+{
+ struct hisilpc_dev *lpcdev;
+ struct lpc_cycle_para iopara;
+ unsigned char *newbuf;
+ unsigned int loopcnt, cntleft;
+ unsigned int max_perburst;
+ int ret = 0;
+
+ if (!devobj || !inbuf || !count || !dlen ||
+ dlen > LPC_MAX_DULEN || (dlen & (dlen - 1)))
+ return -1;
+
+ iopara.opflags = 0;
+ if (dlen > 1)
+ iopara.opflags |= FG_INCRADDR_LPC;
+ iopara.csize = dlen;
+
+ lpcdev = (struct hisilpc_dev *)devobj;
+ newbuf = (unsigned char *)inbuf;
+ /*
+ * ensure data stream whose lenght is multiple of dlen to be processed
+ * each IO input
+ */
+ max_perburst = LPC_MAX_OPCNT & (~(dlen - 1));
+ cntleft = count * dlen;
+ do {
+ loopcnt = (cntleft >= max_perburst) ? max_perburst : cntleft;
+ ret = hisilpc_target_in(lpcdev, &iopara, ptaddr, newbuf,
+ loopcnt);
+ if (ret)
+ break;
+ newbuf += loopcnt;
+ cntleft -= loopcnt;
+ } while (cntleft);
+
+ return ret;
+}
+
+/**
+ * hisilpc_comm_outs - write/output the data in buffer to the I/O peripheral
+ * through LPC, it corresponds to outs(b,w,l)
+ * @devobj: pointer to the device information relevant to LPC controller.
+ * @ptaddr: the target I/O port address.
+ * @outbuf: a buffer where write/output data bytes are stored.
+ * @dlen: the data length required writing to the target I/O port .
+ * @count: how many data units whose length is dlen will be written.
+ *
+ */
+static void hisilpc_comm_outs(void *devobj, unsigned long ptaddr,
+ const void *outbuf, size_t dlen, unsigned int count)
+{
+ struct hisilpc_dev *lpcdev;
+ struct lpc_cycle_para iopara;
+ const unsigned char *newbuf;
+ unsigned int loopcnt, cntleft;
+ unsigned int max_perburst;
+ int ret = 0;
+
+ if (!devobj || !outbuf || !count || !dlen ||
+ dlen > LPC_MAX_DULEN || (dlen & (dlen - 1)))
+ return;
+
+ iopara.opflags = 0;
+ if (dlen > 1)
+ iopara.opflags |= FG_INCRADDR_LPC;
+ iopara.csize = dlen;
+
+ lpcdev = (struct hisilpc_dev *)devobj;
+ newbuf = (unsigned char *)outbuf;
+ /*
+ * ensure data stream whose lenght is multiple of dlen to be processed
+ * each IO input
+ */
+ max_perburst = LPC_MAX_OPCNT & (~(dlen - 1));
+ cntleft = count * dlen;
+ do {
+ loopcnt = (cntleft >= max_perburst) ? max_perburst : cntleft;
+ ret = hisilpc_target_out(lpcdev, &iopara, ptaddr, newbuf,
+ loopcnt);
+ if (ret)
+ break;
+ newbuf += loopcnt;
+ cntleft -= loopcnt;
+ } while (cntleft);
+}
+
+
+/**
+ * hisilpc_probe - the probe callback function for hisi lpc device,
+ * will finish all the intialization.
+ * @pdev: the platform device corresponding to hisi lpc
+ *
+ * Returns 0 on success, non-zero on fail.
+ *
+ */
+static int hisilpc_probe(struct platform_device *pdev)
+{
+ struct resource *iores;
+ struct hisilpc_dev *lpcdev;
+ int ret;
+
+ dev_info(&pdev->dev, "hslpc start probing...\n");
+
+ lpcdev = devm_kzalloc(&pdev->dev,
+ sizeof(struct hisilpc_dev), GFP_KERNEL);
+ if (!lpcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&lpcdev->cycle_lock);
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lpcdev->membase = devm_ioremap_resource(&pdev->dev, iores);
+ if (IS_ERR(lpcdev->membase)) {
+ dev_err(&pdev->dev, "No mem resource for memory mapping!\n");
+ return PTR_ERR(lpcdev->membase);
+ }
+ /*
+ * The first PCIBIOS_MIN_IO is reserved specific for indirectIO.
+ * It will separate indirectIO range from pci host bridge to
+ * avoid the possible PIO conflict.
+ * Set the indirectIO range directly here.
+ */
+ lpcdev->io_ops.start = 0;
+ lpcdev->io_ops.end = PCIBIOS_MIN_IO - 1;
+ lpcdev->io_ops.devpara = lpcdev;
+ lpcdev->io_ops.pfin = hisilpc_comm_in;
+ lpcdev->io_ops.pfout = hisilpc_comm_out;
+ lpcdev->io_ops.pfins = hisilpc_comm_ins;
+ lpcdev->io_ops.pfouts = hisilpc_comm_outs;
+
+ platform_set_drvdata(pdev, lpcdev);
+
+ arm64_set_extops(&lpcdev->io_ops);
+
+ /*
+ * The children scanning is only for dts mode. For ACPI children,
+ * the corresponding devices had be created during acpi scanning.
+ */
+ ret = 0;
+ if (!has_acpi_companion(&pdev->dev))
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
+ if (!ret)
+ dev_info(&pdev->dev, "hslpc end probing. range[0x%lx - %lx]\n",
+ arm64_extio_ops->start, arm64_extio_ops->end);
+ else
+ dev_info(&pdev->dev, "hslpc probing is fail(%d)\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id hisilpc_of_match[] = {
+ {
+ .compatible = "hisilicon,low-pin-count",
+ },
+ {},
+};
+
+static const struct acpi_device_id hisilpc_acpi_match[] = {
+ {"HISI0191", },
+ {},
+};
+
+static struct platform_driver hisilpc_driver = {
+ .driver = {
+ .name = "hisi_lpc",
+ .of_match_table = hisilpc_of_match,
+ .acpi_match_table = hisilpc_acpi_match,
+ },
+ .probe = hisilpc_probe,
+};
+
+
+builtin_platform_driver(hisilpc_driver);
--
1.9.1
^ permalink raw reply related
* [PATCH v4 20/23] ARM: shmobile: rcar-gen2: Stop passing mode pins state to clock driver
From: Sergei Shtylyov @ 2016-11-01 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477055857-17936-21-git-send-email-geert+renesas@glider.be>
On 10/21/2016 04:17 PM, Geert Uytterhoeven wrote:
> Now the R-Car Gen2 CPG clock driver obtains the state of the mode pins
> from the R-Car RST driver, there's no longer a need to pass this state
> explicitly. Hence we can just call of_clk_init() instead.
>
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Acked-by: Dirk Behme <dirk.behme@de.bosch.com>
> ---
> v4:
> - Add Acked-by,
> - Rebase on top of "ARM: shmobile: rcar-gen2: Obtain extal frequency
> from DT",
> - Remove the call to rcar_gen2_read_mode_pins(),
>
> v3:
> - Drop "select MFD_SYSCON", as the clock driver no longer uses syscon,
>
> v2:
> - Kill compiler warning if CONFIG_ARM_ARCH_TIMER is not enabled.
> ---
> arch/arm/mach-shmobile/setup-rcar-gen2.c | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm/mach-shmobile/setup-rcar-gen2.c b/arch/arm/mach-shmobile/setup-rcar-gen2.c
> index afb9fdcd3d9084e2..b527258e0a62e806 100644
> --- a/arch/arm/mach-shmobile/setup-rcar-gen2.c
> +++ b/arch/arm/mach-shmobile/setup-rcar-gen2.c
[...]
> @@ -130,7 +129,7 @@ void __init rcar_gen2_timer_init(void)
> iounmap(base);
> #endif /* CONFIG_ARM_ARCH_TIMER */
>
> - rcar_gen2_clocks_init(mode);
> + of_clk_init(NULL);
> clocksource_probe();
> }
>
This hunk no longer applies to devel.
MBR, Sergei
^ permalink raw reply
* [PATCH 1/1] ARM: dts: imx6ul-14x14-evk: add USB dual-role support
From: Shawn Guo @ 2016-11-01 13:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAL411-p_T9DP61kVJ8yTf3v5tduAgTgPaKa1dBCPDo+RhP7igw@mail.gmail.com>
On Fri, Oct 28, 2016 at 05:17:24PM +0800, Peter Chen wrote:
> On Tue, Aug 2, 2016 at 5:08 PM, Peter Chen <peter.chen@nxp.com> wrote:
> > With commit 851ce932242d ("usb: chipidea: otg: don't wait vbus
> > drops below BSV when starts host"), the driver can support
> > enabling vbus output without software control, so this board
> > (control vbus output through ID pin) can support dual-role now.
> >
> > Signed-off-by: Peter Chen <peter.chen@nxp.com>
> > ---
> > arch/arm/boot/dts/imx6ul-14x14-evk.dts | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/arch/arm/boot/dts/imx6ul-14x14-evk.dts b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> > index e281d50..c5cf942 100644
> > --- a/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> > +++ b/arch/arm/boot/dts/imx6ul-14x14-evk.dts
> > @@ -225,7 +225,7 @@
> > };
> >
> > &usbotg1 {
> > - dr_mode = "peripheral";
> > + dr_mode = "otg";
> > status = "okay";
> > };
> >
>
> Ping....
Applied, thanks. Sorry for missing the patch in the first place.
Shawn
^ permalink raw reply
* [PATCH 01/14] dma: sun6i-dma: Add burst case of 4
From: Koul, Vinod @ 2016-11-01 13:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v67SSZF6XL-HXv83nuwTKpJc53h8gsrb2r2V98LNZBzqEA@mail.gmail.com>
On Sun, 2016-10-30 at 10:06 +0800, Chen-Yu Tsai wrote:
> Looking at the dmaengine API, I believe we got it wrong.
>
> max_burst in dma_slave_config denotes the largest amount of data
> a single transfer should be, as described in dmaengine.h:
Not a single transfer but smallest transaction within a transfer of a
block. So dmaengines transfer data in bursts from source to destination,
this parameter decides the size of that bursts
>
> ?* @src_maxburst: the maximum number of words (note: words, as in
> ?* units of the src_addr_width member, not bytes) that can be sent
> ?* in one burst to the device. Typically something like half the
> ?* FIFO depth on I/O peripherals so you don't overflow it. This
> ?* may or may not be applicable on memory sources.
> ?* @dst_maxburst: same as src_maxburst but for destination target
> ?* mutatis mutandis.
>
> The DMA engine driver should be free to select whatever burst size
> that doesn't exceed this. So for max_burst = 4, the driver can select
> burst = 4 for controllers that do support it, or burst = 1 for those
> that don't, and do more bursts.
Nope, the client configures these parameters and dmaengine driver
validates and programs
>
> This also means we can increase max_burst for the audio codec, as
> the FIFO is 64 samples deep for stereo, or 128 samples for mono.
Beware that higher bursts means chance of underrun of FIFO. This value
is selected with consideration of power and performance required. Lazy
allocation would be half of FIFO size..
--?
~Vinod
^ permalink raw reply
* [PATCH v8 0/3] ARM: dts: imx6q: Add Engicam i.CoreM6 dts
From: Shawn Guo @ 2016-11-01 13:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
On Fri, Oct 21, 2016 at 01:35:50PM +0530, Jagan Teki wrote:
> From: Jagan Teki <jagan@amarulasolutions.com>
>
> This is series add dts support for Engicam I.Core M6 qdl modules. just
> rebased on top of linux-next.
>
> Jagan Teki (3):
> ARM: dts: imx6q: Add Engicam i.CoreM6 Quad/Dual initial support
> ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
> ARM: dts: imx6qdl-icore: Add FEC support
Applied all, thanks.
^ permalink raw reply
* [PATCH v2 1/3] arm64: crypto/aes-ce-ccm: Cleanup hwcap check
From: Ard Biesheuvel @ 2016-11-01 14:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477929825-5907-2-git-send-email-suzuki.poulose@arm.com>
Hi Suzuki,
On 31 October 2016 at 16:03, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> Use the module_cpu_feature_match to make sure the system has
> HWCAP_AES to use the module.
>
> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> arch/arm64/crypto/aes-ce-ccm-glue.c | 5 ++---
> 1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/crypto/aes-ce-ccm-glue.c b/arch/arm64/crypto/aes-ce-ccm-glue.c
> index f4bf2f2..fa82eaa 100644
> --- a/arch/arm64/crypto/aes-ce-ccm-glue.c
> +++ b/arch/arm64/crypto/aes-ce-ccm-glue.c
> @@ -14,6 +14,7 @@
> #include <crypto/algapi.h>
> #include <crypto/scatterwalk.h>
> #include <crypto/internal/aead.h>
> +#include <linux/cpufeature.h>
> #include <linux/module.h>
>
> #include "aes-ce-setkey.h"
> @@ -296,8 +297,6 @@ static struct aead_alg ccm_aes_alg = {
>
> static int __init aes_mod_init(void)
> {
> - if (!(elf_hwcap & HWCAP_AES))
> - return -ENODEV;
> return crypto_register_aead(&ccm_aes_alg);
> }
>
> @@ -306,7 +305,7 @@ static void __exit aes_mod_exit(void)
> crypto_unregister_aead(&ccm_aes_alg);
> }
>
> -module_init(aes_mod_init);
> +module_cpu_feature_match(AES, aes_mod_init);
I don't think this change is correct. This will result in the AES
instruction dependency to be exposed via the module alias, causing the
module to be loaded automatically as soon as udev detects that the CPU
implements those instructions. For plain AES, that makes sense, but
AES in CCM mode is specific to CCMP (WPA2) on mac80211 controllers
that have no hardware AES support, and to IPsec VPN. For this reason,
the algo type is exposed via the module alias instead (i.e,
'ccm(aes)'), which will result in the module being loaded as soon as
the crypto algo manager instantiates the transform. On CPUs that don't
implement the AES instructions, this will fail, and it will fall back
to the generic CCM driver instead.
^ permalink raw reply
* [PATCH v2 2/3] arm64: Add hypervisor safe helper for checking constant capabilities
From: Will Deacon @ 2016-11-01 14:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477929825-5907-3-git-send-email-suzuki.poulose@arm.com>
On Mon, Oct 31, 2016 at 04:03:44PM +0000, Suzuki K Poulose wrote:
> The hypervisor may not have full access to the kernel data structures
> and hence cannot safely use cpus_have_cap() helper for checking the
> system capability. Add a safe helper for hypervisors to check a constant
> system capability, which *doesn't* fall back to checking the bitmap
> maintained by the kernel.
>
> Cc: Marc Zyngier <marc.zyngier@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> arch/arm64/include/asm/cpufeature.h | 16 +++++++++++++---
> 1 file changed, 13 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
> index 758d74f..ae5e994 100644
> --- a/arch/arm64/include/asm/cpufeature.h
> +++ b/arch/arm64/include/asm/cpufeature.h
> @@ -9,8 +9,6 @@
> #ifndef __ASM_CPUFEATURE_H
> #define __ASM_CPUFEATURE_H
>
> -#include <linux/jump_label.h>
> -
> #include <asm/hwcap.h>
> #include <asm/sysreg.h>
>
> @@ -45,6 +43,8 @@
>
> #ifndef __ASSEMBLY__
>
> +#include <linux/bug.h>
> +#include <linux/jump_label.h>
> #include <linux/kernel.h>
>
> /* CPU feature register tracking */
> @@ -122,6 +122,16 @@ static inline bool cpu_have_feature(unsigned int num)
> return elf_hwcap & (1UL << num);
> }
>
> +/* System capability check for constant caps */
> +static inline bool cpus_have_const_cap(int num)
> +{
> + if (__builtin_constant_p(num))
> + return static_branch_unlikely(&cpu_hwcap_keys[num]);
> + BUILD_BUG();
I think you'll already get a build failure if you pass a non-const num
to static_branch_unlikely, so this seems unnecessary. Furthermore, if
we're going to introduce a "const-only" version of this function, maybe
it's best to kill the __builtin_constant_p checks altogether, including
in the existing cpus_have_cap code? That way, the caller can make the
decision about whether or not they want to use static keys.
> + /* unreachable */
> + return false;
> +}
> +
> static inline bool cpus_have_cap(unsigned int num)
> {
> if (num >= ARM64_NCAPS)
It seems odd to check aginst ARM64_NCAPS here, but not in your new function.
Is the check actually necessary in either case? If so, we should probably
duplicate it for consistency.
Will
^ permalink raw reply
* [PATCH v2] ARM: dts: socfpga: Add Macnica sodia board
From: Dinh Nguyen @ 2016-11-01 14:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOesGMgkVWNZ0Nq0c-GZXbJETVBKOo2m5gZ1u8gUtb0tUDeqjQ@mail.gmail.com>
Hi Olof,
On 10/27/2016 07:23 PM, Olof Johansson wrote:
> Hi,
>
> Saw this when looking at the patch when merging it.
>
> Please fix up with incremental patch. Dinh, this goes for other
> platforms under socfpga too, several have this problem:
>
> On Sat, Sep 24, 2016 at 4:59 PM, Nobuhiro Iwamatsu <iwamatsu@nigauri.org> wrote:
>> diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sodia.dts b/arch/arm/boot/dts/socfpga_cyclone5_sodia.dts
>> new file mode 100644
>> index 0000000..9aaf413
>> --- /dev/null
>> +++ b/arch/arm/boot/dts/socfpga_cyclone5_sodia.dts
>> @@ -0,0 +1,123 @@
>> +/*
>> + * Copyright (C) 2016 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
>> + *
>> + * 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
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "socfpga_cyclone5.dtsi"
>> +#include <dt-bindings/gpio/gpio.h>
>> +#include <dt-bindings/input/input.h>
>> +
>> +/ {
>> + model = "Altera SOCFPGA Cyclone V SoC Macnica Sodia board";
>> + compatible = "altr,socfpga-cyclone5", "altr,socfpga";
>
> You should add a compatible entry for your specific board. Compatible
> goes from specific to generic, so something like:
>
> compatible = "altr,macnica-sodia", "latr,socfpga-cyclone5", "altr,socfpga";
>
> You can even add entries for the specific rev (if there are
> differences). But at the very least, add one for the board.
>
>
> Dinh, please follow up for other boards as well. I've still merged the
> pull request you sent (email about that separately).
>
Thanks for noticing this. I'll send a patch to fix this shortly.
Dinh
^ permalink raw reply
* [PATCH 1/5] ARM: dts: imx: add Boundary Devices Nitrogen6_SOM2 support
From: Shawn Guo @ 2016-11-01 14:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161025211955.5345-2-gary.bisson@boundarydevices.com>
On Tue, Oct 25, 2016 at 11:19:51PM +0200, Gary Bisson wrote:
> SoM based on i.MX6 Quad with 1GB of DDR3.
>
> https://boundarydevices.com/product/nit6x-som-v2/
>
> Signed-off-by: Gary Bisson <gary.bisson@boundarydevices.com>
> ---
> arch/arm/boot/dts/Makefile | 1 +
> arch/arm/boot/dts/imx6q-nitrogen6_som2.dts | 53 ++
> arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi | 770 ++++++++++++++++++++++++++
> 3 files changed, 824 insertions(+)
> create mode 100644 arch/arm/boot/dts/imx6q-nitrogen6_som2.dts
> create mode 100644 arch/arm/boot/dts/imx6qdl-nitrogen6_som2.dtsi
<snip>
> +/ {
> + chosen {
> + stdout-path = &uart2;
> + };
> +
> + memory {
> + reg = <0x10000000 0x40000000>;
> + };
> +
> + backlight_lcd: backlight_lcd {
Please use hyphen instead of underscore in node name. Please check
through the patch series.
Shawn
> + compatible = "pwm-backlight";
> + pwms = <&pwm1 0 5000000>;
> + brightness-levels = <0 4 8 16 32 64 128 255>;
> + default-brightness-level = <7>;
> + power-supply = <®_3p3v>;
> + status = "okay";
> + };
^ permalink raw reply
* [PATCH 3/5] ARM: dts: imx6qdl-sabrelite: add missing USB PHY reset control
From: Shawn Guo @ 2016-11-01 14:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAAMH-yu1oC8qaAtVwSxJxKxSmXMzMJkUeu4cUu3OB2yhDV8GBg@mail.gmail.com>
On Tue, Oct 25, 2016 at 11:53:40PM +0200, Gary Bisson wrote:
> Hi Fabio,
>
> On Tue, Oct 25, 2016 at 11:28 PM, Fabio Estevam <festevam@gmail.com> wrote:
> > Hi Gary,
> >
> > On Tue, Oct 25, 2016 at 7:19 PM, Gary Bisson
> > <gary.bisson@boundarydevices.com> wrote:
> >> Declared as a regulator since the driver doesn't have a reset-gpios
> >> property for this.
> >
> > Peter Chen is working on adding USB reset-gpio property this. Please
> > check his series:
> > https://www.spinics.net/lists/arm-kernel/msg536105.html
>
> Thanks, I wasn't aware of this series. Indeed if this power sequence
> code gets upstream soon I guess we can drop both patches about the USB
> PHY reset.
Let's wait then instead of adding something that will be removed later.
Shawn
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox