* [PATCH v5 4/7] of: Reduce indentation in of_graph_get_next_endpoint
From: Philipp Zabel @ 2014-02-27 17:35 UTC (permalink / raw)
To: Grant Likely, Mauro Carvalho Chehab, Russell King - ARM Linux
Cc: Rob Herring, Sylwester Nawrocki, Laurent Pinchart,
Guennadi Liakhovetski, Tomi Valkeinen, Kyungmin Park,
linux-kernel, linux-media, devicetree, Philipp Zabel
In-Reply-To: <1393522540-22887-1-git-send-email-p.zabel@pengutronix.de>
A 'return endpoint;' at the end of the (!prev) case allows to
reduce the indentation level of the (prev) case.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
drivers/of/base.c | 42 ++++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 6e650cf..8ecca7a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2026,32 +2026,34 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
pr_err("%s(): no endpoint nodes specified for %s\n",
__func__, parent->full_name);
of_node_put(node);
- } else {
- port = of_get_parent(prev);
- if (WARN_ONCE(!port, "%s(): endpoint has no parent node\n",
- __func__))
- return NULL;
- /* Avoid dropping prev node refcount to 0. */
- of_node_get(prev);
- endpoint = of_get_next_child(port, prev);
- if (endpoint) {
- of_node_put(port);
- return endpoint;
- }
+ return endpoint;
+ }
- /* No more endpoints under this port, try the next one. */
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (of_node_cmp(port->name, "port"));
+ port = of_get_parent(prev);
+ if (WARN_ONCE(!port, "%s(): endpoint has no parent node\n",
+ __func__))
+ return NULL;
- /* Pick up the first endpoint in this port. */
- endpoint = of_get_next_child(port, NULL);
+ /* Avoid dropping prev node refcount to 0. */
+ of_node_get(prev);
+ endpoint = of_get_next_child(port, prev);
+ if (endpoint) {
of_node_put(port);
+ return endpoint;
}
+ /* No more endpoints under this port, try the next one. */
+ do {
+ port = of_get_next_child(parent, port);
+ if (!port)
+ return NULL;
+ } while (of_node_cmp(port->name, "port"));
+
+ /* Pick up the first endpoint in this port. */
+ endpoint = of_get_next_child(port, NULL);
+ of_node_put(port);
+
return endpoint;
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);
--
1.8.5.3
^ permalink raw reply related
* [PATCH v5 5/7] [media] of: move common endpoint parsing to drivers/of
From: Philipp Zabel @ 2014-02-27 17:35 UTC (permalink / raw)
To: Grant Likely, Mauro Carvalho Chehab, Russell King - ARM Linux
Cc: Rob Herring, Sylwester Nawrocki, Laurent Pinchart,
Guennadi Liakhovetski, Tomi Valkeinen, Kyungmin Park,
linux-kernel, linux-media, devicetree, Philipp Zabel
In-Reply-To: <1393522540-22887-1-git-send-email-p.zabel@pengutronix.de>
This patch adds a new struct of_endpoint which is then embedded in struct
v4l2_of_endpoint and contains the endpoint properties that are not V4L2
(or even media) specific: the port number, endpoint id, local device tree
node and remote endpoint phandle. of_graph_parse_endpoint parses those
properties and is used by v4l2_of_parse_endpoint, which just adds the
V4L2 MBUS information to the containing v4l2_of_endpoint structure.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Changes since v4:
- Fixed users of struct v4l2_of_endpoint
- Removed left-over #include <media/of_graph.h> from v4l2-of.h
---
drivers/media/platform/exynos4-is/media-dev.c | 10 ++++-----
drivers/media/platform/exynos4-is/mipi-csis.c | 2 +-
drivers/media/v4l2-core/v4l2-of.c | 16 +++-----------
drivers/of/base.c | 31 +++++++++++++++++++++++++++
include/linux/of_graph.h | 20 +++++++++++++++++
include/media/v4l2-of.h | 8 ++-----
6 files changed, 62 insertions(+), 25 deletions(-)
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index d0f82da..04d6ecd 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -469,10 +469,10 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
return 0;
v4l2_of_parse_endpoint(ep, &endpoint);
- if (WARN_ON(endpoint.port == 0) || index >= FIMC_MAX_SENSORS)
+ if (WARN_ON(endpoint.base.port == 0) || index >= FIMC_MAX_SENSORS)
return -EINVAL;
- pd->mux_id = (endpoint.port - 1) & 0x1;
+ pd->mux_id = (endpoint.base.port - 1) & 0x1;
rem = of_graph_get_remote_port_parent(ep);
of_node_put(ep);
@@ -494,13 +494,13 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
return -EINVAL;
}
- if (fimc_input_is_parallel(endpoint.port)) {
+ if (fimc_input_is_parallel(endpoint.base.port)) {
if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
else
pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_656;
pd->flags = endpoint.bus.parallel.flags;
- } else if (fimc_input_is_mipi_csi(endpoint.port)) {
+ } else if (fimc_input_is_mipi_csi(endpoint.base.port)) {
/*
* MIPI CSI-2: only input mux selection and
* the sensor's clock frequency is needed.
@@ -508,7 +508,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
pd->sensor_bus_type = FIMC_BUS_TYPE_MIPI_CSI2;
} else {
v4l2_err(&fmd->v4l2_dev, "Wrong port id (%u) at node %s\n",
- endpoint.port, rem->full_name);
+ endpoint.base.port, rem->full_name);
}
/*
* For FIMC-IS handled sensors, that are placed under i2c-isp device
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index fd1ae65..3678ba5 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -772,7 +772,7 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
/* Get port node and validate MIPI-CSI channel id. */
v4l2_of_parse_endpoint(node, &endpoint);
- state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0;
+ state->index = endpoint.base.port - FIMC_INPUT_MIPI_CSI2_0;
if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
return -ENXIO;
diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c
index f919db3..b4ed9a9 100644
--- a/drivers/media/v4l2-core/v4l2-of.c
+++ b/drivers/media/v4l2-core/v4l2-of.c
@@ -127,17 +127,9 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node,
int v4l2_of_parse_endpoint(const struct device_node *node,
struct v4l2_of_endpoint *endpoint)
{
- struct device_node *port_node = of_get_parent(node);
-
- memset(endpoint, 0, offsetof(struct v4l2_of_endpoint, head));
-
- endpoint->local_node = node;
- /*
- * It doesn't matter whether the two calls below succeed.
- * If they don't then the default value 0 is used.
- */
- of_property_read_u32(port_node, "reg", &endpoint->port);
- of_property_read_u32(node, "reg", &endpoint->id);
+ of_graph_parse_endpoint(node, &endpoint->base);
+ endpoint->bus_type = 0;
+ memset(&endpoint->bus, 0, sizeof(endpoint->bus));
v4l2_of_parse_csi_bus(node, endpoint);
/*
@@ -147,8 +139,6 @@ int v4l2_of_parse_endpoint(const struct device_node *node,
if (endpoint->bus.mipi_csi2.flags == 0)
v4l2_of_parse_parallel_bus(node, endpoint);
- of_node_put(port_node);
-
return 0;
}
EXPORT_SYMBOL(v4l2_of_parse_endpoint);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 8ecca7a..ba3cfca 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1985,6 +1985,37 @@ struct device_node *of_find_next_cache_node(const struct device_node *np)
}
/**
+ * of_graph_parse_endpoint() - parse common endpoint node properties
+ * @node: pointer to endpoint device_node
+ * @endpoint: pointer to the OF endpoint data structure
+ *
+ * All properties are optional. If none are found, we don't set any flags.
+ * This means the port has a static configuration and no properties have
+ * to be specified explicitly.
+ * The caller should hold a reference to @node.
+ */
+int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint)
+{
+ struct device_node *port_node = of_get_parent(node);
+
+ memset(endpoint, 0, sizeof(*endpoint));
+
+ endpoint->local_node = node;
+ /*
+ * It doesn't matter whether the two calls below succeed.
+ * If they don't then the default value 0 is used.
+ */
+ of_property_read_u32(port_node, "reg", &endpoint->port);
+ of_property_read_u32(node, "reg", &endpoint->id);
+
+ of_node_put(port_node);
+
+ return 0;
+}
+EXPORT_SYMBOL(of_graph_parse_endpoint);
+
+/**
* of_graph_get_next_endpoint() - get next endpoint node
* @parent: pointer to the parent device node
* @prev: previous endpoint node, or NULL to get first
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 3bbeb60..2b233db 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -14,7 +14,21 @@
#ifndef __LINUX_OF_GRAPH_H
#define __LINUX_OF_GRAPH_H
+/**
+ * struct of_endpoint - the OF graph endpoint data structure
+ * @port: identifier (value of reg property) of a port this endpoint belongs to
+ * @id: identifier (value of reg property) of this endpoint
+ * @local_node: pointer to device_node of this endpoint
+ */
+struct of_endpoint {
+ unsigned int port;
+ unsigned int id;
+ const struct device_node *local_node;
+};
+
#ifdef CONFIG_OF
+int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint);
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *previous);
struct device_node *of_graph_get_remote_port_parent(
@@ -22,6 +36,12 @@ struct device_node *of_graph_get_remote_port_parent(
struct device_node *of_graph_get_remote_port(const struct device_node *node);
#else
+static inline int of_graph_parse_endpoint(const struct device_node *node,
+ struct of_endpoint *endpoint);
+{
+ return -ENOSYS;
+}
+
static inline struct device_node *of_graph_get_next_endpoint(
const struct device_node *parent,
struct device_node *previous)
diff --git a/include/media/v4l2-of.h b/include/media/v4l2-of.h
index 3a49735..70fa7b7 100644
--- a/include/media/v4l2-of.h
+++ b/include/media/v4l2-of.h
@@ -51,17 +51,13 @@ struct v4l2_of_bus_parallel {
/**
* struct v4l2_of_endpoint - the endpoint data structure
- * @port: identifier (value of reg property) of a port this endpoint belongs to
- * @id: identifier (value of reg property) of this endpoint
- * @local_node: pointer to device_node of this endpoint
+ * @base: struct of_endpoint containing port, id, and local of_node
* @bus_type: bus type
* @bus: bus configuration data structure
* @head: list head for this structure
*/
struct v4l2_of_endpoint {
- unsigned int port;
- unsigned int id;
- const struct device_node *local_node;
+ struct of_endpoint base;
enum v4l2_mbus_type bus_type;
union {
struct v4l2_of_bus_parallel parallel;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v5 6/7] of: Implement simplified graph binding for single port devices
From: Philipp Zabel @ 2014-02-27 17:35 UTC (permalink / raw)
To: Grant Likely, Mauro Carvalho Chehab, Russell King - ARM Linux
Cc: Rob Herring, Sylwester Nawrocki, Laurent Pinchart,
Guennadi Liakhovetski, Tomi Valkeinen, Kyungmin Park,
linux-kernel, linux-media, devicetree, Philipp Zabel
In-Reply-To: <1393522540-22887-1-git-send-email-p.zabel@pengutronix.de>
For simple devices with only one port, it can be made implicit.
The endpoint node can be a direct child of the device node.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
drivers/of/base.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index ba3cfca..7d9c62b 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -2037,8 +2037,13 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
struct device_node *node;
/*
* It's the first call, we have to find a port subnode
- * within this node or within an optional 'ports' node.
+ * within this node or within an optional 'ports' node,
+ * or at least a single endpoint.
*/
+ endpoint = of_get_child_by_name(parent, "endpoint");
+ if (endpoint)
+ return endpoint;
+
node = of_get_child_by_name(parent, "ports");
if (node)
parent = node;
@@ -2049,8 +2054,6 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
/* Found a port, get an endpoint. */
endpoint = of_get_next_child(port, NULL);
of_node_put(port);
- } else {
- endpoint = NULL;
}
if (!endpoint)
@@ -2065,6 +2068,10 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
if (WARN_ONCE(!port, "%s(): endpoint has no parent node\n",
__func__))
return NULL;
+ if (port == parent) {
+ of_node_put(port);
+ return NULL;
+ }
/* Avoid dropping prev node refcount to 0. */
of_node_get(prev);
@@ -2105,9 +2112,11 @@ struct device_node *of_graph_get_remote_port_parent(
/* Get remote endpoint node. */
np = of_parse_phandle(node, "remote-endpoint", 0);
- /* Walk 3 levels up only if there is 'ports' node. */
+ /* Walk 3 levels up only if there is 'ports' node */
for (depth = 3; depth && np; depth--) {
np = of_get_next_parent(np);
+ if (depth == 3 && of_node_cmp(np->name, "port"))
+ break;
if (depth == 2 && of_node_cmp(np->name, "ports"))
break;
}
@@ -2130,6 +2139,11 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
np = of_parse_phandle(node, "remote-endpoint", 0);
if (!np)
return NULL;
- return of_get_next_parent(np);
+ np = of_get_next_parent(np);
+ if (of_node_cmp(np->name, "port")) {
+ of_node_put(np);
+ return NULL;
+ }
+ return np;
}
EXPORT_SYMBOL(of_graph_get_remote_port);
--
1.8.5.3
^ permalink raw reply related
* [PATCH v5 7/7] of: Document simplified graph binding for single port devices
From: Philipp Zabel @ 2014-02-27 17:35 UTC (permalink / raw)
To: Grant Likely, Mauro Carvalho Chehab, Russell King - ARM Linux
Cc: Rob Herring, Sylwester Nawrocki, Laurent Pinchart,
Guennadi Liakhovetski, Tomi Valkeinen, Kyungmin Park,
linux-kernel, linux-media, devicetree, Philipp Zabel
In-Reply-To: <1393522540-22887-1-git-send-email-p.zabel@pengutronix.de>
For simple devices with only one port, it can be made implicit.
The endpoint node can be a direct child of the device node.
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
---
Documentation/devicetree/bindings/graph.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/graph.txt b/Documentation/devicetree/bindings/graph.txt
index 554865b..6dfaff8 100644
--- a/Documentation/devicetree/bindings/graph.txt
+++ b/Documentation/devicetree/bindings/graph.txt
@@ -84,6 +84,14 @@ device {
};
};
+For devices with only a single port and a single endpoint, this can be further
+simplified by making the port implicit, and adding the endpoint node as a direct
+child of the device node.
+
+device {
+ endpoint { ... };
+};
+
Links between endpoints
-----------------------
--
1.8.5.3
^ permalink raw reply related
* RE: [PATCH 2/4] net: rfkill: gpio: remove gpio names
From: Gross, Mark @ 2014-02-27 17:38 UTC (permalink / raw)
To: Linus Walleij, Stephen Warren, Alexandre Courbot, Grant Likely,
devicetree@vger.kernel.org
Cc: Chen-Yu Tsai, Heikki Krogerus, Johannes Berg, David S. Miller,
Rhyland Klein, linux-wireless, netdev, linux-kernel,
Arnd Bergmann, Westerberg, Mika, mgross@linux.intel.com
In-Reply-To: <CACRpkdZBvKU31EDX=6=wVLXaQDAqD6VHtaE_mGnFLr1UYQAH9g@mail.gmail.com>
Please know that no one should not consider me an authority on ACPI at this time. But, I have some comments / context / thoughts below.
Also I apologize in advance for any email formatting issues caused by replying to this via my work exchange account / outlook client. Folks can use mgross@linux.intel.com to avoid outlook-isms from me in the future.
> -----Original Message-----
> From: Linus Walleij [mailto:linus.walleij@linaro.org]
> Sent: Tuesday, February 25, 2014 1:14 AM
> To: Stephen Warren; Alexandre Courbot; Grant Likely;
> devicetree@vger.kernel.org
> Cc: Chen-Yu Tsai; Heikki Krogerus; Johannes Berg; David S. Miller; Rhyland
> Klein; linux-wireless; netdev; linux-kernel; Arnd Bergmann; Gross, Mark
> Subject: Re: [PATCH 2/4] net: rfkill: gpio: remove gpio names
>
> On Fri, Feb 21, 2014 at 6:35 AM, Stephen Warren
> <swarren@wwwdotorg.org> wrote:
> > On 02/20/2014 06:55 PM, Chen-Yu Tsai wrote:
>
> >> That's correct. However using con_id to pass this results in
> >> different behavior across DT and ACPI. A better way is to export the
> >> labeling function so consumers can set meaningful labels themselves.
> >
> > But this code is the consumer of those GPIOs. IF the parameter to
> > devm_gpiod_get_index() isn't intended to be used, why does it exist?
>
> Kerneldoc says:
>
> /**
> * gpiod_get_index - obtain a GPIO from a multi-index GPIO function
> * @dev: GPIO consumer, can be NULL for system-global GPIOs
> * @con_id: function within the GPIO consumer
> * @idx: index of the GPIO to obtain in the consumer
> *
>
> Basically it is just exposing the fact that of_find_gpio() and
> acpi_find_gpio() both take a con_id as argument.
>
> If we drill into this, we find that it is used to conjure the arbitrary string
> before the gpios in the DT case, like:
>
> foo-gpios = <...>;
>
> As in tegra30-beaver.dts...
>
> sdhci@78000000 {
> status = "okay";
> cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>;
> wp-gpios = <&gpio TEGRA_GPIO(T, 3) GPIO_ACTIVE_HIGH>;
> power-gpios = <&gpio TEGRA_GPIO(D, 7) GPIO_ACTIVE_HIGH>;
> bus-width = <4>;
> };
>
> Instead of passing the GPIOs as index 0,1,2 they are named and I do admit
> this has a nice "things are under control" aspect to it.
[Gross, Mark] FWIW I don't think this is as "under control" as you do. Those names in the above sdhci example are derived from a specific SDHCI tegra spec sheet or schematic. Those names likely come from the data sheet for the controller. I would like SDHCI kernel drivers to work pretty much the same for all compatible controllers. The set of compatible controllers have spec sheets that use different naming conventions for their pins and thus a name space pollution of the SDHCI enumeration ABI will be a problem.
>
> In the ACPI case the con_id is not used for anything.
>
> So it is basically there to satisfy the habit in some device tree bindings to
> name gpio arrays instead of just passing gpios = <...>; (The latter should be
> encouraged going forward.)
[Gross, Mark] This is in fact just an idiom of the ACPI ABI inherited from writing linux drivers that work on systems with BIOS/FW developed for MS-Windows. For the devices I work on we have a number of driver teams filing bugs against the BIOS/FW to add named GPIO objects to device name spaces such that they can directly query for the GPIO their driver needs and avoid assuming the 3rd GPIO is the special one they need for whatever...
So if you have the luxury of being able to influence (file bugs against or write) the platform enumeration ABI then with ACPI you can have a named gpio today. Note: there is an expectation that the _PRP feature to go into the next version of ACPI and that should enable a trivial 1-1 mapping (when _prp is used by the ACPI platform) between the different platform enumeration API's (DT and ACPI).
Still, from a platform enumeration ABI point of view I see the arbitrariness of indexes not much worse than the arbitrary naming of pins off of spec sheets or schematics. Given that the authors of those specs/layouts come up with a new naming schemes every time they log into their workstations (especially for the schematics). I do admit names are a little better but still have the scaling issue WRT namespaces and arbitrary naming by HW engineers over time.
>
> As DT is ABI we need to keep this forever, and driver may need to look for
> foo-gpios=<> and gpios=<> alike for backward compatibility if we'd change it,
> sweet isn't it? :-)
>
> I don't quite like this, as it is adding stupid nonsens arguments for ACPI GPIO
> producers (which only take an index AFAICT), but it is a first sacrifice on the
> altar of trying to mask the differences between DT and ACPI probe paths
> about which I have mixed feelings.
[Gross, Mark] I don't have mixed feelings. I want to see converged probe paths that can handle both ACPI and DT platform ABI's seamlessly so we can reuse drivers across architectures effectively. I think the _PRP will help with that even though its use assumes you have influence over the platform FW and this will not help with supporting of legacy BIOS's will burden us with gpio index assumptions.
--mark
>
> Yours,
> LInus Walleij
^ permalink raw reply
* Re: [PATCH 2/4] net: rfkill: gpio: remove gpio names
From: Stephen Warren @ 2014-02-27 17:47 UTC (permalink / raw)
To: Gross, Mark, Linus Walleij, Alexandre Courbot, Grant Likely,
devicetree@vger.kernel.org
Cc: Chen-Yu Tsai, Heikki Krogerus, Johannes Berg, David S. Miller,
Rhyland Klein, linux-wireless, netdev, linux-kernel,
Arnd Bergmann, Westerberg, Mika, mgross@linux.intel.com
In-Reply-To: <BD75D217E5BAC84080F8EFB2582976378B327178@ORSMSX109.amr.corp.intel.com>
On 02/27/2014 10:38 AM, Gross, Mark wrote:
> Please know that no one should not consider me an authority on ACPI at this time. But, I have some comments / context / thoughts below.
>
> Also I apologize in advance for any email formatting issues caused by replying to this via my work exchange account / outlook client. Folks can use mgross@linux.intel.com to avoid outlook-isms from me in the future.
>
>> -----Original Message-----
>> From: Linus Walleij [mailto:linus.walleij@linaro.org]
>> Sent: Tuesday, February 25, 2014 1:14 AM
>> To: Stephen Warren; Alexandre Courbot; Grant Likely;
>> devicetree@vger.kernel.org
>> Cc: Chen-Yu Tsai; Heikki Krogerus; Johannes Berg; David S. Miller; Rhyland
>> Klein; linux-wireless; netdev; linux-kernel; Arnd Bergmann; Gross, Mark
>> Subject: Re: [PATCH 2/4] net: rfkill: gpio: remove gpio names
>>
>> On Fri, Feb 21, 2014 at 6:35 AM, Stephen Warren
>> <swarren@wwwdotorg.org> wrote:
>>> On 02/20/2014 06:55 PM, Chen-Yu Tsai wrote:
>>
>>>> That's correct. However using con_id to pass this results in
>>>> different behavior across DT and ACPI. A better way is to export the
>>>> labeling function so consumers can set meaningful labels themselves.
>>>
>>> But this code is the consumer of those GPIOs. IF the parameter to
>>> devm_gpiod_get_index() isn't intended to be used, why does it exist?
>>
>> Kerneldoc says:
>>
>> /**
>> * gpiod_get_index - obtain a GPIO from a multi-index GPIO function
>> * @dev: GPIO consumer, can be NULL for system-global GPIOs
>> * @con_id: function within the GPIO consumer
>> * @idx: index of the GPIO to obtain in the consumer
>> *
>>
>> Basically it is just exposing the fact that of_find_gpio() and
>> acpi_find_gpio() both take a con_id as argument.
>>
>> If we drill into this, we find that it is used to conjure the arbitrary string
>> before the gpios in the DT case, like:
>>
>> foo-gpios = <...>;
>>
>> As in tegra30-beaver.dts...
>>
>> sdhci@78000000 {
>> status = "okay";
>> cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>;
>> wp-gpios = <&gpio TEGRA_GPIO(T, 3) GPIO_ACTIVE_HIGH>;
>> power-gpios = <&gpio TEGRA_GPIO(D, 7) GPIO_ACTIVE_HIGH>;
>> bus-width = <4>;
>> };
>>
>> Instead of passing the GPIOs as index 0,1,2 they are named and I do admit
>> this has a nice "things are under control" aspect to it.
>
> [Gross, Mark] FWIW I don't think this is as "under control" as you do. Those
> names in the above sdhci example are derived from a specific SDHCI
tegra spec
> sheet or schematic. Those names likely come from the data sheet for
> the controller.
The names of the properties are fixed and defined by the DT binding for
the Tegra SDHCI controller, or even the core SDHCI bindings. Hence, they
will be the same in every DT file that uses that Tegra SDHCI compatible
value (the compatible property isn't show above, because the above
fragment is a board.dts file, and the compatible value gets inherited
from the soc.dtsi file). There won't be any variation at all,
irrespective of what signal names exist in a particular board schematic.
If there were ever an (upstream?) ACPI "binding"(?) for the Tegra SDHCI
controller, I would hope it would use the exact same names for the GPIO
signals.
^ permalink raw reply
* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Srikanth Thokala @ 2014-02-27 18:04 UTC (permalink / raw)
To: Jassi Brar
Cc: devicetree, Levente Kurusa, Lars-Peter Clausen, Koul, Vinod, lkml,
Michal Simek, Srikanth Thokala, dmaengine, Rob Herring,
Grant Likely, Williams, Dan J, Andy Shevchenko,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAJe_ZhcyYaZk+=JWyV_nphrWt+17Kmb9rZ153f0tDEeD_kFW8A@mail.gmail.com>
On Wed, Feb 26, 2014 at 8:32 PM, Jassi Brar <jaswinder.singh@linaro.org> wrote:
> On 26 February 2014 23:21, Srikanth Thokala <sthokal@xilinx.com> wrote:
>> On Mon, Feb 24, 2014 at 7:39 AM, Jassi Brar <jaswinder.singh@linaro.org> wrote:
>>> On 21 February 2014 23:37, Srikanth Thokala <sthokal@xilinx.com> wrote:
>>>> On Thu, Feb 20, 2014 at 3:23 PM, Jassi Brar <jaswinder.singh@linaro.org>
>>>> wrote:
>>>>> On 20 February 2014 14:54, Srikanth Thokala <sthokal@xilinx.com> wrote:
>>>>>> On Wed, Feb 19, 2014 at 12:33 AM, Jassi Brar <jaswinder.singh@linaro.org>
>>>>>> wrote:
>>>>>>> On 18 February 2014 23:16, Srikanth Thokala <sthokal@xilinx.com> wrote:
>>>>>>>> On Tue, Feb 18, 2014 at 10:20 PM, Jassi Brar
>>>>>>>> <jaswinder.singh@linaro.org> wrote:
>>>>>>>>> On 18 February 2014 16:58, Srikanth Thokala <sthokal@xilinx.com>
>>>>>>>>> wrote:
>>>>>>>>>> On Mon, Feb 17, 2014 at 3:27 PM, Jassi Brar
>>>>>>>>>> <jaswinder.singh@linaro.org> wrote:
>>>>>>>>>>> On 15 February 2014 17:30, Srikanth Thokala <sthokal@xilinx.com>
>>>>>>>>>>> wrote:
>>>>>>>>>>>> The current implementation of interleaved DMA API support multiple
>>>>>>>>>>>> frames only when the memory is contiguous by incrementing
>>>>>>>>>>>> src_start/
>>>>>>>>>>>> dst_start members of interleaved template.
>>>>>>>>>>>>
>>>>>>>>>>>> But, when the memory is non-contiguous it will restrict slave
>>>>>>>>>>>> device
>>>>>>>>>>>> to not submit multiple frames in a batch. This patch handles this
>>>>>>>>>>>> issue by allowing the slave device to send array of interleaved dma
>>>>>>>>>>>> templates each having a different memory location.
>>>>>>>>>>>>
>>>>>>>>>>> How fragmented could be memory in your case? Is it inefficient to
>>>>>>>>>>> submit separate transfers for each segment/frame?
>>>>>>>>>>> It will help if you could give a typical example (chunk size and gap
>>>>>>>>>>> in bytes) of what you worry about.
>>>>>>>>>>
>>>>>>>>>> With scatter-gather engine feature in the hardware, submitting
>>>>>>>>>> separate
>>>>>>>>>> transfers for each frame look inefficient. As an example, our DMA
>>>>>>>>>> engine
>>>>>>>>>> supports up to 16 video frames, with each frame (a typical video
>>>>>>>>>> frame
>>>>>>>>>> size) being contiguous in memory but frames are scattered into
>>>>>>>>>> different
>>>>>>>>>> locations. We could not definitely submit frame by frame as it would
>>>>>>>>>> be
>>>>>>>>>> software overhead (HW interrupting for each frame) resulting in video
>>>>>>>>>> lags.
>>>>>>>>>>
>>>>>>>>> IIUIC, it is 30fps and one dma interrupt per frame ... it doesn't seem
>>>>>>>>> inefficient at all. Even poor-latency audio would generate a higher
>>>>>>>>> interrupt-rate. So the "inefficiency concern" doesn't seem valid to
>>>>>>>>> me.
>>>>>>>>>
>>>>>>>>> Not to mean we shouldn't strive to reduce the interrupt-rate further.
>>>>>>>>> Another option is to emulate the ring-buffer scheme of ALSA.... which
>>>>>>>>> should be possible since for a session of video playback the frame
>>>>>>>>> buffers' locations wouldn't change.
>>>>>>>>>
>>>>>>>>> Yet another option is to use the full potential of the
>>>>>>>>> interleaved-xfer api as such. It seems you confuse a 'video frame'
>>>>>>>>> with the interleaved-xfer api's 'frame'. They are different.
>>>>>>>>>
>>>>>>>>> Assuming your one video frame is F bytes long and Gk is the gap in
>>>>>>>>> bytes between end of frame [k] and start of frame [k+1] and Gi != Gj
>>>>>>>>> for i!=j
>>>>>>>>> In the context of interleaved-xfer api, you have just 1 Frame of 16
>>>>>>>>> chunks. Each chunk is Fbytes and the inter-chunk-gap(ICG) is Gk where
>>>>>>>>> 0<=k<15
>>>>>>>>> So for your use-case .....
>>>>>>>>> dma_interleaved_template.numf = 1 /* just 1 frame */
>>>>>>>>> dma_interleaved_template.frame_size = 16 /* containing 16 chunks */
>>>>>>>>> ...... //other parameters
>>>>>>>>>
>>>>>>>>> You have 3 options to choose from and all should work just as fine.
>>>>>>>>> Otherwise please state your problem in real numbers (video-frames'
>>>>>>>>> size, count & gap in bytes).
>>>>>>>>
>>>>>>>> Initially I interpreted interleaved template the same. But, Lars
>>>>>>>> corrected me
>>>>>>>> in the subsequent discussion and let me put it here briefly,
>>>>>>>>
>>>>>>>> In the interleaved template, each frame represents a line of size
>>>>>>>> denoted by
>>>>>>>> chunk.size and the stride by icg. 'numf' represent number of frames
>>>>>>>> i.e.
>>>>>>>> number of lines.
>>>>>>>>
>>>>>>>> In video frame context,
>>>>>>>> chunk.size -> hsize
>>>>>>>> chunk.icg -> stride
>>>>>>>> numf -> vsize
>>>>>>>> and frame_size is always 1 as it will have only one chunk in a line.
>>>>>>>>
>>>>>>> But you said in your last post
>>>>>>> "with each frame (a typical video frame size) being contiguous in
>>>>>>> memory"
>>>>>>> ... which is not true from what you write above. Anyways, my first 2
>>>>>>> suggestions still hold.
>>>>>>
>>>>>> Yes, each video frame is contiguous and they can be scattered.
>>>>>>
>>>>> I assume by contiguous frame you mean as in framebuffer? Which is an
>>>>> array of bytes.
>>>>> If yes, then you should do as I suggest first, frame_size=16 and numf=1.
>>>>
>>>> I think am confusing you. I would like to explain with an example. Lets
>>>> say
>>>> each video frame is 4k size starting at address 0x10004000 (-0x10005000) and
>>>> other frame at 0x20002000 (-0x20003000), and so on.
>>>>
>>> As I said plz dont confuse video frame with DMA frame.... in video
>>> frame the stride is constant(zero or not) whereas in DMA context the
>>> stride must be zero for the frame to be called contiguous.
>>>
>>>> So, the frames are
>>>> scattered in memory and as the template doesnt allow multiple src_start/
>>>> dst_start we could not use single template to fill the HW descriptors (of
>>>> frames). So, I feel your suggestion might not work if the frames are
>>>> scattered.
>>>> Also, how could we get 'vsize' value in your approach?
>>>>
>>> The client driver(video driver) should know the frame parameters.
>>> Practically you'll have to populate 16 transfer templates (frame
>>> attributes and locations won't change for a session) and submit to be
>>> transferred _cyclically_.
>>>
>>>> More importantly,
>>>> we are overriding the semantics of interleaved template members.
>>>>
>>> Not at all. Interleaved-dma isn't meant for only constant stride/icg
>>> transfers. Rather it's for identical frames with random strides.
>>>
>>>>
>>>>>
>>>>> If no, then it seems you are already doing the right thing.... the
>>>>> ring-buffer scheme. Please share some stats how the current api is
>>>>> causing you overhead because that is a very common case (many
>>>>> controllers support LLI) and you have 467ms (@30fps with 16-frames
>>>>> ring-buffer) to queue in before you see any frame drop.
>>>>
>>>> As I mentioned earlier in the thread, our hardware has a SG engine where by
>>>> we could send multiple frames in a batch. Using the original implementation
>>>> of interleaved API, we have three options to transfer.
>>>>
>>>> One is to send frame by frame to the hardware. We get a async_tx desc for
>>>> each frame and then we submit it to hardware triggering it to transfer this
>>>> BD.
>>>> We queue the next descriptor on the pending queue and whenever there is
>>>> a completion interrupt we submit the next BD on this queue to hardware. In
>>>> this implementation we are not efficiently using the SG engine in the
>>>> hardware
>>>> as we transferring frame by frame even HW allows us to transfer multiple
>>>> frames.
>>>>
>>> Sending one frame at a time will likely cause jitters. That shouldn't be done.
>>>
>>>> The second option is to queue all the BDs until the maximum frames that HW
>>>> is
>>>> capable to transfer in a batch and then submit to SG engine in the HW. With
>>>> this approach I feel there will be additional software overhead to track the
>>>> number
>>>> of maximum transfers and few additional cycles to release the cookie of each
>>>> desc.
>>>> Here, each desc represents a frame.
>>>>
>>> APIs are written for the gcd of h/w. We should optimize only for
>>> majority and here isn't even anything gained after changing the api.
>>> What you want to 'optimize' has been there since ever. Nobody
>>> considers that overhead. BTW you don't even want to spend a few 'extra
>>> cycles' but what about every other platform that doesn't support this
>>> and will have to scan for such transfer requests?
>>>
>>>> The last option is the current implementation of the driver along with this
>>>> change in
>>>> API. It will allow us to send array of interleaved templates wherein we
>>>> could allocate
>>>> a single async desc which will handle multiple frames (or segments) and just
>>>> submit
>>>> this desc to HW. Then we program the current to first frame, tail to last
>>>> frame and
>>>> HW will complete this transfer. Here, each desc represents multiple frames.
>>>>
>>>> My point here is the driver should use hardware resources efficiently and I
>>>> feel the
>>>> driver will be inefficient if we dont use them.
>>>>
>>> I am afraid you are getting carried away by the 'awesomeness' of your
>>> hardware. RingBuffers/Cyclic transfers are meant for cases just like
>>> yours.
>>> Consider the following ... if you queue 16 frames and don't care to
>>> track before all are transmitted, you'll have very high latency. Video
>>> seeks will take unacceptably long and give the impression of a slow
>>> system. Whereas if you get callbacks for each frame rendered, you
>>> could updates frames from the next one thereby having very quick
>>> response time.
>>
>> Not at all, at least in our hardware, when we submit transfers it is most
>> likely to be completed. There are few errors, but mostly are recoverable
>> and it doesnt stop the transfer unless there is a critical error which needs
>> a reset to the whole system. So, at least in my use case there will be
>> no latency. I am not saying hardware is great, but it is the IP implementation.
>>
> I am not talking about the error cases.
> Apply your patch locally so that you queue 16frames and not get
> notified upon each frame 'rendered'... now click on 'seek bar' of the
> video player. See how slow it is to jump to play from the new
> location. Or if the frames are to be encoded after dma transfer...
> see the 'padding' that would need to be done at the end. These
> concerns are common with audio subsystem using ring-buffers.
> Anyways that is just FYI, I don't care how you implement your platform.
>
>> Regarding this API change, I had earlier explained my use case in my
>> v2 thread.
>> Lars and Vinod came up with this resolution to allow array of
>> interleaved templates.
>> I also feel this is reasonable change for the subsystem.
>>
> I don't see you getting any better performance from your hardware and
> I certainly don't find your usecase anything new (many dma controllers
> support LLI and they work just fine as such). And I have already
> explained how you could do, whatever you want, without this change.
> So a polite NAK from me.
Ok. I would go with your suggestion of having frame_size = 16 and will
send v4 dropping this change.
Thanks for the valuable suggestions.
Srikanth
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [PATCH v12 0/4] PHY: Add APM X-Gene SoC 15Gbps Multi-purpose PHY support
From: Loc Ho @ 2014-02-27 18:14 UTC (permalink / raw)
To: kishon, tj
Cc: olof, arnd, linux-kernel, linux-scsi, linux-ide, devicetree,
linux-arm-kernel, ddutile, jcm, patches, Loc Ho, Tuan Phan,
Suman Tripathi
This patch adds support for APM X-Gene SoC 15Gbps Multi-purpose PHY. This
is the physical layer interface for the corresponding host controller. This
driver uses the PHY generic framework. In addition, the PHY generic
framework is patched to provide an function to set the speed of the PHY.
v12:
* Add driver depend on HAS_IOMEM and OF
* Fix comment typo in header file phy-xgene.
* Change all macro shift value in hex to decimal
* Add time out for while loop to function sds_wr and sds_rd
* Set proper return value if failed to get IO resource 0
* Move devm_of_phy_provider_register to last operation in probe function
* Remove driver registration print statement
* Replace module init/exit with module_platform_driver
* Change license to GPL v2
v11:
* Add comment to function phy_set_speed
* Add commit log for documentation patch file
* Minor comment update to function xgene_phy_force_lat_summer_cal and
xgene_phy_sata_force_gen
v10
* Update comment for function xgene_phy_force_lat_summer_cal and
xgene_phy_sata_force_gen with function style fully-winged style
v9
* Update CMU parameter setting for register 13
* Add required delay when configure CMU PLL, Manual Calibration PLL, and VCO
PLL
* Add comment for CMU PLL calibration loop delay of 10us
* Add required delay for stopping and starting summer calibrations
* Update comment for summer and latch calibration delays
* Update comment for PHY reset Rx delay and decrease max sleep time from 500
to 150us
* Always program the DFE (equalizer) setting to 0x7e00 as with original version
* Fix Tx speed selection to always using Gen3 setting when force to an
specified generation speed
v8
* Update binding documentation
* Remove XGENE_PHY_DTS and XGENE_PHY_EXT_DTS defines
* Remove support for internal clock
* Remove support for external reference CMU
* Remove the need for external reference resource DTS entry and its related
code
v7
* Add/Update PHY CMU/lane parameters and its default values
* Rename variable enable_manual_cal to preA3Chip
* Remove function phy_rd, phy_wr, and phy_wr_flush
* Change function cmu_wr, cmu_rd, cmu_toggle1to0, cmu_clrbits, cmu_setbits,
serdes_wr, serdes_rd, serdes_clrbits, and serdes_setbits to take context
instead void *
* Remove function serdes_toggle1to0
* Decrease the polling time from 10ms to 1ms on CMU calibration complete
detection
* Move all SATA specify code in function xgene_phy_hw_initialize into
function xgene_phy_hw_init_sata
* Add usleep_range after starting summer/latch calibrations
* Add usleep_range between receiver reset (function xgene_phy_reset_rxd)
* Save and restore PHY register 31 instead writing 0 in function
xgene_phy_gen_avg_val
* Update function xgene_phy_sata_force_gen programming sequences
* Add support to reset the receiver lane in function xgene_phy_set_speed
if speed is 0
* Update PHY parameters in DTS per controller
* Some minor code clean up
v6
* Move PHY document to Documentation/devicetree/binding/phy
* Remove _ADDR from all register defines
* Update clock-names property for sataphy1clk, sataphy2clk, and sataphy3clk
v5
* Update DTS binding documentation
* Remove direct clock access and use clock interface instead
* Change override parameters to decimal instead hex values
* Change apm,tx-amplitude, apm,tx-pre-cursor1, apm,tx-pre-cursor2,
apm,tx-post-cursor to be unit of uV
v4
* Update documentation with 'apm,' instead 'apm-'
* Change DTS override parameter to have 'apm,'
* Add select GENERIC_PHY to Kconfig PHY_XGENE
* Make override parameters to be pair of three values instead one
* Some minor comment and indentation changes
* Remove error register addition offset
* Add ULL to constants
* Use module_init instead subsys_initcall
* Make DTS node based on first register address
* Update override setting values
v3
* Major re-write of the code based on various review comments
* Support external clock only at the moment
* Support SATA mode only at the moment
* No UEFI support at the moment
v2
* Remove port knowledge from functions
* Make all functions static
* Remove ID completely
* Make resource requirement based on compatible type
* Rename override PHY parameters with more descriptive name
* Add override PHY parameter for per controller, per port, and per speed
* Patch the generic PHY frame to expose set_speed operation
v1
* Initial version
Signed-off-by: Loc Ho <lho@apm.com>
Signed-off-by: Tuan Phan <tphan@apm.com>
Signed-off-by: Suman Tripathi <stripathi@apm.com>
---
Loc Ho (4):
PHY: Add function set_speed to generic PHY framework
Documentation: Add APM X-Gene SoC 15Gbps Multi-purpose PHY driver
binding documentation
PHY: add APM X-Gene SoC 15Gbps Multi-purpose PHY driver
arm64: Add APM X-Gene SoC 15Gbps Multi-purpose PHY DTS entries
.../devicetree/bindings/phy/apm-xgene-phy.txt | 79 +
arch/arm64/boot/dts/apm-storm.dtsi | 75 +
drivers/phy/Kconfig | 7 +
drivers/phy/Makefile | 2 +
drivers/phy/phy-core.c | 30 +
drivers/phy/phy-xgene.c | 1813 ++++++++++++++++++++
include/linux/phy/phy.h | 8 +
7 files changed, 2014 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
create mode 100644 drivers/phy/phy-xgene.c
^ permalink raw reply
* [PATCH v12 1/4] PHY: Add function set_speed to generic PHY framework
From: Loc Ho @ 2014-02-27 18:14 UTC (permalink / raw)
To: kishon, tj
Cc: olof, arnd, linux-kernel, linux-scsi, linux-ide, devicetree,
linux-arm-kernel, ddutile, jcm, patches, Loc Ho
In-Reply-To: <1393524848-8207-1-git-send-email-lho@apm.com>
This patch adds function set_speed to the generic PHY framework operation
structure. This function can be called to instruct the PHY underlying layer
at specified lane to configure for specified speed in hertz.
Signed-off-by: Loc Ho <lho@apm.com>
---
drivers/phy/phy-core.c | 30 ++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 8 ++++++++
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 645c867..5451b6d 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -258,6 +258,36 @@ int phy_power_off(struct phy *phy)
EXPORT_SYMBOL_GPL(phy_power_off);
/**
+ * phy_set_speed - set an specified lane at an specified speed
+ * @phy: phy instance to be set
+ * @lane: zero-based lane index
+ * @speed: operating speed in hz
+ *
+ * Returns 0 if successful, -ENOTSUPP if not supported,
+ * -EINVAL if parameter out of range.
+ */
+int phy_set_speed(struct phy *phy, int lane, u64 speed)
+{
+ int ret = -ENOTSUPP;
+
+ mutex_lock(&phy->mutex);
+ if (phy->ops->set_speed) {
+ ret = phy->ops->set_speed(phy, lane, speed);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy set speed failed --> %d\n",
+ ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set_speed);
+
+/**
* of_phy_get() - lookup and obtain a reference to a phy by phandle
* @dev: device that requests this phy
* @index: the index of the phy
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index e273e5a..4eb589c 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -27,6 +27,7 @@ struct phy;
* @exit: operation to be performed while exiting
* @power_on: powering on the phy
* @power_off: powering off the phy
+ * @set_speed: set operation speed in hz
* @owner: the module owner containing the ops
*/
struct phy_ops {
@@ -34,6 +35,7 @@ struct phy_ops {
int (*exit)(struct phy *phy);
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
+ int (*set_speed)(struct phy *phy, int lane, u64 speed);
struct module *owner;
};
@@ -145,6 +147,7 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width)
{
phy->attrs.bus_width = bus_width;
}
+int phy_set_speed(struct phy *phy, int lane, u64 speed);
struct phy *phy_get(struct device *dev, const char *string);
struct phy *devm_phy_get(struct device *dev, const char *string);
void phy_put(struct phy *phy);
@@ -227,6 +230,11 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width)
return;
}
+static inline int phy_set_speed(struct phy *phy, int lane, u64 speed)
+{
+ return -ENOSYS;
+}
+
static inline struct phy *phy_get(struct device *dev, const char *string)
{
return ERR_PTR(-ENOSYS);
--
1.5.5
^ permalink raw reply related
* [PATCH v12 2/4] Documentation: Add APM X-Gene SoC 15Gbps Multi-purpose PHY driver binding documentation
From: Loc Ho @ 2014-02-27 18:14 UTC (permalink / raw)
To: kishon, tj
Cc: olof, arnd, linux-kernel, linux-scsi, linux-ide, devicetree,
linux-arm-kernel, ddutile, jcm, patches, Loc Ho, Tuan Phan,
Suman Tripathi
In-Reply-To: <1393524848-8207-2-git-send-email-lho@apm.com>
This patch adds APM X-Gene SoC 15Gbps Multi-purpose PHY driver binding documentation.
Signed-off-by: Loc Ho <lho@apm.com>
Signed-off-by: Tuan Phan <tphan@apm.com>
Signed-off-by: Suman Tripathi <stripathi@apm.com>
---
.../devicetree/bindings/phy/apm-xgene-phy.txt | 79 ++++++++++++++++++++
1 files changed, 79 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
diff --git a/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt b/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
new file mode 100644
index 0000000..5f3a65a
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
@@ -0,0 +1,79 @@
+* APM X-Gene 15Gbps Multi-purpose PHY nodes
+
+PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
+PHY (pair of lanes) has its own node.
+
+Required properties:
+- compatible : Shall be "apm,xgene-phy".
+- reg : PHY memory resource is the SDS PHY access resource.
+- #phy-cells : Shall be 1 as it expects one argument for setting
+ the mode of the PHY. Possible values are 0 (SATA),
+ 1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
+
+Optional properties:
+- status : Shall be "ok" if enabled or "disabled" if disabled.
+ Default is "ok".
+- clocks : Reference to the clock entry.
+- apm,tx-eye-tuning : Manual control to fine tune the capture of the serial
+ bit lines from the automatic calibrated position.
+ Two set of 3-tuple setting for each (up to 3)
+ supported link speed on the host. Range from 0 to
+ 127 in unit of one bit period. Default is 10.
+- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
+ data earlier than the nominal sampling point. 1 means
+ sample data later than the nominal sampling point.
+ Two set of 3-tuple setting for each (up to 3)
+ supported link speed on the host. Default is 0.
+- apm,tx-boost-gain : Frequency boost AC (LSB 3-bit) and DC (2-bit)
+ gain control. Two set of 3-tuple setting for each
+ (up to 3) supported link speed on the host. Range is
+ between 0 to 31 in unit of dB. Default is 3.
+- apm,tx-amplitude : Amplitude control. Two set of 3-tuple setting for
+ each (up to 3) supported link speed on the host.
+ Range is between 0 to 199500 in unit of uV.
+ Default is 199500 uV.
+- apm,tx-pre-cursor1 : 1st pre-cursor emphasis taps control. Two set of
+ 3-tuple setting for each (up to 3) supported link
+ speed on the host. Range is 0 to 273000 in unit of
+ uV. Default is 0.
+- apm,tx-pre-cursor2 : 2st pre-cursor emphasis taps control. Two set of
+ 3-tuple setting for each (up to 3) supported link
+ speed on the host. Range is 0 to 127400 in unit uV.
+ Default is 0x0.
+- apm,tx-post-cursor : Post-cursor emphasis taps control. Two set of
+ 3-tuple setting for Gen1, Gen2, and Gen3. Range is
+ between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
+- apm,tx-speed : Tx operating speed. One set of 3-tuple for each
+ supported link speed on the host.
+ 0 = 1-2Gbps
+ 1 = 2-4Gbps (1st tuple default)
+ 2 = 4-8Gbps
+ 3 = 8-15Gbps (2nd tuple default)
+ 4 = 2.5-4Gbps
+ 5 = 4-5Gbps
+ 6 = 5-6Gbps
+ 7 = 6-16Gbps (3rd tuple default)
+
+NOTE: PHY override parameters are board specific setting.
+
+Example:
+ phy1: phy@1f21a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f21a000 0x0 0x100>;
+ #phy-cells = <1>;
+ status = "disabled";
+ };
+
+ phy2: phy@1f22a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f22a000 0x0 0x100>;
+ #phy-cells = <1>;
+ status = "ok";
+ };
+
+ phy3: phy@1f23a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f23a000 0x0 0x100>;
+ #phy-cells = <1>;
+ status = "ok";
+ };
--
1.5.5
^ permalink raw reply related
* [PATCH v12 3/4] PHY: add APM X-Gene SoC 15Gbps Multi-purpose PHY driver
From: Loc Ho @ 2014-02-27 18:14 UTC (permalink / raw)
To: kishon, tj
Cc: olof, arnd, linux-kernel, linux-scsi, linux-ide, devicetree,
linux-arm-kernel, ddutile, jcm, patches, Loc Ho, Tuan Phan,
Suman Tripathi
In-Reply-To: <1393524848-8207-3-git-send-email-lho@apm.com>
This patch adds support for APM X-Gene SoC 15Gbps Multi-purpose PHY.
This is the physical layer interface for the corresponding host
controller. Currently, only external clock and SATA mode
are supported.
Signed-off-by: Loc Ho <lho@apm.com>
Signed-off-by: Tuan Phan <tphan@apm.com>
Signed-off-by: Suman Tripathi <stripathi@apm.com>
---
drivers/phy/Kconfig | 7 +
drivers/phy/Makefile | 2 +
drivers/phy/phy-xgene.c | 1813 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1822 insertions(+), 0 deletions(-)
create mode 100644 drivers/phy/phy-xgene.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index afa2354..221a1b7 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -64,4 +64,11 @@ config BCM_KONA_USB2_PHY
help
Enable this to support the Broadcom Kona USB 2.0 PHY.
+config PHY_XGENE
+ tristate "APM X-Gene 15Gbps PHY support"
+ depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ This option enables support for APM X-Gene SoC multi-purpose PHY.
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c253..dee70f4 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
+obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
+
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
new file mode 100644
index 0000000..470e0b1
--- /dev/null
+++ b/drivers/phy/phy-xgene.c
@@ -0,0 +1,1813 @@
+/*
+ * AppliedMicro X-Gene Multi-purpose PHY driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ * Tuan Phan <tphan@apm.com>
+ * Suman Tripathi <stripathi@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by 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/>.
+ *
+ * The APM X-Gene PHY consists of two PLL clock macro's (CMU) and lanes.
+ * The first PLL clock macro is used for internal reference clock. The second
+ * PLL clock macro is used to generate the clock for the PHY. This driver
+ * configures the first PLL CMU, the second PLL CMU, and programs the PHY to
+ * operate according to the mode of operation. The first PLL CMU is only
+ * required if internal clock is enabled.
+ *
+ * Logical Layer Out Of HW module units:
+ *
+ * -----------------
+ * | Internal | |------|
+ * | Ref PLL CMU |----| | ------------- ---------
+ * ------------ ---- | MUX |-----|PHY PLL CMU|----| Serdes|
+ * | | | | ---------
+ * External Clock ------| | -------------
+ * |------|
+ *
+ * The Ref PLL CMU CSR (Configuration System Registers) is accessed
+ * indirectly from the SDS offset at 0x2000. It is only required for
+ * internal reference clock.
+ * The PHY PLL CMU CSR is accessed indirectly from the SDS offset at 0x0000.
+ * The Serdes CSR is accessed indirectly from the SDS offset at 0x0400.
+ *
+ * The Ref PLL CMU can be located within the same PHY IP or outside the PHY IP
+ * due to shared Ref PLL CMU. For PHY with Ref PLL CMU shared with another IP,
+ * it is located outside the PHY IP. This is the case for the PHY located
+ * at 0x1f23a000 (SATA Port 4/5). For such PHY, another resource is required
+ * to located the SDS/Ref PLL CMU module and its clock for that IP enabled.
+ *
+ * Currently, this driver only supports SATA mode with external clock.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+
+/* Max 2 lanes per a PHY unit */
+#define MAX_LANE 2
+
+/* Register offset inside the PHY */
+#define SERDES_PLL_INDIRECT_OFFSET 0x0000
+#define SERDES_PLL_REF_INDIRECT_OFFSET 0x2000
+#define SERDES_INDIRECT_OFFSET 0x0400
+#define SERDES_LANE_STRIDE 0x0200
+
+/* Some default Serdes parameters */
+#define DEFAULT_SATA_TXBOOST_GAIN { 0x1e, 0x1e, 0x1e }
+#define DEFAULT_SATA_TXEYEDIRECTION { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXEYETUNING { 0xa, 0xa, 0xa }
+#define DEFAULT_SATA_SPD_SEL { 0x1, 0x3, 0x7 }
+#define DEFAULT_SATA_TXAMP { 0x8, 0x8, 0x8 }
+#define DEFAULT_SATA_TXCN1 { 0x2, 0x2, 0x2 }
+#define DEFAULT_SATA_TXCN2 { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXCP1 { 0xa, 0xa, 0xa }
+
+#define SATA_SPD_SEL_GEN3 0x7
+#define SATA_SPD_SEL_GEN2 0x3
+#define SATA_SPD_SEL_GEN1 0x1
+
+#define SSC_DISABLE 0
+#define SSC_ENABLE 1
+
+#define FBDIV_VAL_50M 0x77
+#define REFDIV_VAL_50M 0x1
+#define FBDIV_VAL_100M 0x3B
+#define REFDIV_VAL_100M 0x0
+
+/* SATA Clock/Reset CSR */
+#define SATACLKENREG 0x00000000
+#define SATA0_CORE_CLKEN 0x00000002
+#define SATA1_CORE_CLKEN 0x00000004
+#define SATASRESETREG 0x00000004
+#define SATA_MEM_RESET_MASK 0x00000020
+#define SATA_MEM_RESET_RD(src) (((src) & 0x00000020) >> 5)
+#define SATA_SDS_RESET_MASK 0x00000004
+#define SATA_CSR_RESET_MASK 0x00000001
+#define SATA_CORE_RESET_MASK 0x00000002
+#define SATA_PMCLK_RESET_MASK 0x00000010
+#define SATA_PCLK_RESET_MASK 0x00000008
+
+/* SDS CSR used for PHY Indirect access */
+#define SATA_ENET_SDS_PCS_CTL0 0x00000000
+#define REGSPEC_CFG_I_TX_WORDMODE0_SET(dst, src) \
+ (((dst) & ~0x00070000) | (((u32) (src) << 16) & 0x00070000))
+#define REGSPEC_CFG_I_RX_WORDMODE0_SET(dst, src) \
+ (((dst) & ~0x00e00000) | (((u32) (src) << 21) & 0x00e00000))
+#define SATA_ENET_SDS_CTL0 0x0000000c
+#define REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(dst, src) \
+ (((dst) & ~0x00007fff) | (((u32) (src)) & 0x00007fff))
+#define SATA_ENET_SDS_CTL1 0x00000010
+#define CFG_I_SPD_SEL_CDR_OVR1_SET(dst, src) \
+ (((dst) & ~0x0000000f) | (((u32) (src)) & 0x0000000f))
+#define SATA_ENET_SDS_RST_CTL 0x00000024
+#define SATA_ENET_SDS_IND_CMD_REG 0x0000003c
+#define CFG_IND_WR_CMD_MASK 0x00000001
+#define CFG_IND_RD_CMD_MASK 0x00000002
+#define CFG_IND_CMD_DONE_MASK 0x00000004
+#define CFG_IND_ADDR_SET(dst, src) \
+ (((dst) & ~0x003ffff0) | (((u32) (src) << 4) & 0x003ffff0))
+#define SATA_ENET_SDS_IND_RDATA_REG 0x00000040
+#define SATA_ENET_SDS_IND_WDATA_REG 0x00000044
+#define SATA_ENET_CLK_MACRO_REG 0x0000004c
+#define I_RESET_B_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src)) & 0x00000001))
+#define I_PLL_FBDIV_SET(dst, src) \
+ (((dst) & ~0x001ff000) | (((u32) (src) << 12) & 0x001ff000))
+#define I_CUSTOMEROV_SET(dst, src) \
+ (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define O_PLL_LOCK_RD(src) (((src) & 0x40000000) >> 30)
+#define O_PLL_READY_RD(src) (((src) & 0x80000000) >> 31)
+
+/* PLL Clock Macro Unit (CMU) CSR accessing from SDS indirectly */
+#define CMU_REG0 0x00000
+#define CMU_REG0_PLL_REF_SEL_MASK 0x00002000
+#define CMU_REG0_PLL_REF_SEL_SET(dst, src) \
+ (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define CMU_REG0_PDOWN_MASK 0x00004000
+#define CMU_REG0_CAL_COUNT_RESOL_SET(dst, src) \
+ (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define CMU_REG1 0x00002
+#define CMU_REG1_PLL_CP_SET(dst, src) \
+ (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define CMU_REG1_PLL_MANUALCAL_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define CMU_REG1_PLL_CP_SEL_SET(dst, src) \
+ (((dst) & ~0x000003e0) | (((u32) (src) << 5) & 0x000003e0))
+#define CMU_REG1_REFCLK_CMOS_SEL_MASK 0x00000001
+#define CMU_REG1_REFCLK_CMOS_SEL_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG2 0x00004
+#define CMU_REG2_PLL_REFDIV_SET(dst, src) \
+ (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define CMU_REG2_PLL_LFRES_SET(dst, src) \
+ (((dst) & ~0x0000001e) | (((u32) (src) << 1) & 0x0000001e))
+#define CMU_REG2_PLL_FBDIV_SET(dst, src) \
+ (((dst) & ~0x00003fe0) | (((u32) (src) << 5) & 0x00003fe0))
+#define CMU_REG3 0x00006
+#define CMU_REG3_VCOVARSEL_SET(dst, src) \
+ (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define CMU_REG3_VCO_MOMSEL_INIT_SET(dst, src) \
+ (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define CMU_REG3_VCO_MANMOMSEL_SET(dst, src) \
+ (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define CMU_REG4 0x00008
+#define CMU_REG5 0x0000a
+#define CMU_REG5_PLL_LFSMCAP_SET(dst, src) \
+ (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define CMU_REG5_PLL_LOCK_RESOLUTION_SET(dst, src) \
+ (((dst) & ~0x0000000e) | (((u32) (src) << 1) & 0x0000000e))
+#define CMU_REG5_PLL_LFCAP_SET(dst, src) \
+ (((dst) & ~0x00003000) | (((u32) (src) << 12) & 0x00003000))
+#define CMU_REG5_PLL_RESETB_MASK 0x00000001
+#define CMU_REG6 0x0000c
+#define CMU_REG6_PLL_VREGTRIM_SET(dst, src) \
+ (((dst) & ~0x00000600) | (((u32) (src) << 9) & 0x00000600))
+#define CMU_REG6_MAN_PVT_CAL_SET(dst, src) \
+ (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define CMU_REG7 0x0000e
+#define CMU_REG7_PLL_CALIB_DONE_RD(src) ((0x00004000 & (u32) (src)) >> 14)
+#define CMU_REG7_VCO_CAL_FAIL_RD(src) ((0x00000c00 & (u32) (src)) >> 10)
+#define CMU_REG8 0x00010
+#define CMU_REG9 0x00012
+#define CMU_REG9_WORD_LEN_8BIT 0x000
+#define CMU_REG9_WORD_LEN_10BIT 0x001
+#define CMU_REG9_WORD_LEN_16BIT 0x002
+#define CMU_REG9_WORD_LEN_20BIT 0x003
+#define CMU_REG9_WORD_LEN_32BIT 0x004
+#define CMU_REG9_WORD_LEN_40BIT 0x005
+#define CMU_REG9_WORD_LEN_64BIT 0x006
+#define CMU_REG9_WORD_LEN_66BIT 0x007
+#define CMU_REG9_TX_WORD_MODE_CH1_SET(dst, src) \
+ (((dst) & ~0x00000380) | (((u32) (src) << 7) & 0x00000380))
+#define CMU_REG9_TX_WORD_MODE_CH0_SET(dst, src) \
+ (((dst) & ~0x00000070) | (((u32) (src) << 4) & 0x00000070))
+#define CMU_REG9_PLL_POST_DIVBY2_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define CMU_REG9_VBG_BYPASSB_SET(dst, src) \
+ (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define CMU_REG9_IGEN_BYPASS_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define CMU_REG10 0x00014
+#define CMU_REG10_VREG_REFSEL_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG11 0x00016
+#define CMU_REG12 0x00018
+#define CMU_REG12_STATE_DELAY9_SET(dst, src) \
+ (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define CMU_REG13 0x0001a
+#define CMU_REG14 0x0001c
+#define CMU_REG15 0x0001e
+#define CMU_REG16 0x00020
+#define CMU_REG16_PVT_DN_MAN_ENA_MASK 0x00000001
+#define CMU_REG16_PVT_UP_MAN_ENA_MASK 0x00000002
+#define CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(dst, src) \
+ (((dst) & ~0x0000001c) | (((u32) (src) << 2) & 0x0000001c))
+#define CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(dst, src) \
+ (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define CMU_REG16_BYPASS_PLL_LOCK_SET(dst, src) \
+ (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG17 0x00022
+#define CMU_REG17_PVT_CODE_R2A_SET(dst, src) \
+ (((dst) & ~0x00007f00) | (((u32) (src) << 8) & 0x00007f00))
+#define CMU_REG17_RESERVED_7_SET(dst, src) \
+ (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define CMU_REG17_PVT_TERM_MAN_ENA_MASK 0x00008000
+#define CMU_REG18 0x00024
+#define CMU_REG19 0x00026
+#define CMU_REG20 0x00028
+#define CMU_REG21 0x0002a
+#define CMU_REG22 0x0002c
+#define CMU_REG23 0x0002e
+#define CMU_REG24 0x00030
+#define CMU_REG25 0x00032
+#define CMU_REG26 0x00034
+#define CMU_REG26_FORCE_PLL_LOCK_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG27 0x00036
+#define CMU_REG28 0x00038
+#define CMU_REG29 0x0003a
+#define CMU_REG30 0x0003c
+#define CMU_REG30_LOCK_COUNT_SET(dst, src) \
+ (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define CMU_REG30_PCIE_MODE_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define CMU_REG31 0x0003e
+#define CMU_REG32 0x00040
+#define CMU_REG32_FORCE_VCOCAL_START_MASK 0x00004000
+#define CMU_REG32_PVT_CAL_WAIT_SEL_SET(dst, src) \
+ (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define CMU_REG32_IREF_ADJ_SET(dst, src) \
+ (((dst) & ~0x00000180) | (((u32) (src) << 7) & 0x00000180))
+#define CMU_REG33 0x00042
+#define CMU_REG34 0x00044
+#define CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(dst, src) \
+ (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(dst, src) \
+ (((dst) & ~0x00000f00) | (((u32) (src) << 8) & 0x00000f00))
+#define CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(dst, src) \
+ (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(dst, src) \
+ (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define CMU_REG35 0x00046
+#define CMU_REG35_PLL_SSC_MOD_SET(dst, src) \
+ (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define CMU_REG36 0x00048
+#define CMU_REG36_PLL_SSC_EN_SET(dst, src) \
+ (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define CMU_REG36_PLL_SSC_VSTEP_SET(dst, src) \
+ (((dst) & ~0x0000ffc0) | (((u32) (src) << 6) & 0x0000ffc0))
+#define CMU_REG36_PLL_SSC_DSMSEL_SET(dst, src) \
+ (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG37 0x0004a
+#define CMU_REG38 0x0004c
+#define CMU_REG39 0x0004e
+
+/* PHY lane CSR accessing from SDS indirectly */
+#define RXTX_REG0 0x000
+#define RXTX_REG0_CTLE_EQ_HR_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG0_CTLE_EQ_QR_SET(dst, src) \
+ (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define RXTX_REG0_CTLE_EQ_FR_SET(dst, src) \
+ (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG1 0x002
+#define RXTX_REG1_RXACVCM_SET(dst, src) \
+ (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define RXTX_REG1_CTLE_EQ_SET(dst, src) \
+ (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define RXTX_REG1_RXVREG1_SET(dst, src) \
+ (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define RXTX_REG1_RXIREF_ADJ_SET(dst, src) \
+ (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define RXTX_REG2 0x004
+#define RXTX_REG2_VTT_ENA_SET(dst, src) \
+ (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG2_TX_FIFO_ENA_SET(dst, src) \
+ (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define RXTX_REG2_VTT_SEL_SET(dst, src) \
+ (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define RXTX_REG4 0x008
+#define RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK 0x00000040
+#define RXTX_REG4_TX_DATA_RATE_SET(dst, src) \
+ (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define RXTX_REG4_TX_WORD_MODE_SET(dst, src) \
+ (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG5 0x00a
+#define RXTX_REG5_TX_CN1_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG5_TX_CP1_SET(dst, src) \
+ (((dst) & ~0x000007e0) | (((u32) (src) << 5) & 0x000007e0))
+#define RXTX_REG5_TX_CN2_SET(dst, src) \
+ (((dst) & ~0x0000001f) | (((u32) (src) << 0) & 0x0000001f))
+#define RXTX_REG6 0x00c
+#define RXTX_REG6_TXAMP_CNTL_SET(dst, src) \
+ (((dst) & ~0x00000780) | (((u32) (src) << 7) & 0x00000780))
+#define RXTX_REG6_TXAMP_ENA_SET(dst, src) \
+ (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define RXTX_REG6_RX_BIST_ERRCNT_RD_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define RXTX_REG6_TX_IDLE_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG6_RX_BIST_RESYNC_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG7 0x00e
+#define RXTX_REG7_RESETB_RXD_MASK 0x00000100
+#define RXTX_REG7_RESETB_RXA_MASK 0x00000080
+#define RXTX_REG7_BIST_ENA_RX_SET(dst, src) \
+ (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define RXTX_REG7_RX_WORD_MODE_SET(dst, src) \
+ (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG8 0x010
+#define RXTX_REG8_CDR_LOOP_ENA_SET(dst, src) \
+ (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define RXTX_REG8_CDR_BYPASS_RXLOS_SET(dst, src) \
+ (((dst) & ~0x00000800) | (((u32) (src) << 11) & 0x00000800))
+#define RXTX_REG8_SSC_ENABLE_SET(dst, src) \
+ (((dst) & ~0x00000200) | (((u32) (src) << 9) & 0x00000200))
+#define RXTX_REG8_SD_VREF_SET(dst, src) \
+ (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define RXTX_REG8_SD_DISABLE_SET(dst, src) \
+ (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG7 0x00e
+#define RXTX_REG7_RESETB_RXD_SET(dst, src) \
+ (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG7_RESETB_RXA_SET(dst, src) \
+ (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK 0x00004000
+#define RXTX_REG7_LOOP_BACK_ENA_CTLE_SET(dst, src) \
+ (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define RXTX_REG11 0x016
+#define RXTX_REG11_PHASE_ADJUST_LIMIT_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG12 0x018
+#define RXTX_REG12_LATCH_OFF_ENA_SET(dst, src) \
+ (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define RXTX_REG12_SUMOS_ENABLE_SET(dst, src) \
+ (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define RXTX_REG12_RX_DET_TERM_ENABLE_MASK 0x00000002
+#define RXTX_REG12_RX_DET_TERM_ENABLE_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG13 0x01a
+#define RXTX_REG14 0x01c
+#define RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(dst, src) \
+ (((dst) & ~0x0000003f) | (((u32) (src) << 0) & 0x0000003f))
+#define RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(dst, src) \
+ (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define RXTX_REG26 0x034
+#define RXTX_REG26_PERIOD_ERROR_LATCH_SET(dst, src) \
+ (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG26_BLWC_ENA_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG21 0x02a
+#define RXTX_REG21_DO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define RXTX_REG21_XO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(src) ((0x0000000f & (u32)(src)))
+#define RXTX_REG22 0x02c
+#define RXTX_REG22_SO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG22_EO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(src) ((0x0000000f & (u32)(src)))
+#define RXTX_REG23 0x02e
+#define RXTX_REG23_DE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define RXTX_REG23_XE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG24 0x030
+#define RXTX_REG24_EE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define RXTX_REG24_SE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG27 0x036
+#define RXTX_REG28 0x038
+#define RXTX_REG31 0x03e
+#define RXTX_REG38 0x04c
+#define RXTX_REG38_CUSTOMER_PINMODE_INV_SET(dst, src) \
+ (((dst) & 0x0000fffe) | (((u32) (src) << 1) & 0x0000fffe))
+#define RXTX_REG39 0x04e
+#define RXTX_REG40 0x050
+#define RXTX_REG41 0x052
+#define RXTX_REG42 0x054
+#define RXTX_REG43 0x056
+#define RXTX_REG44 0x058
+#define RXTX_REG45 0x05a
+#define RXTX_REG46 0x05c
+#define RXTX_REG47 0x05e
+#define RXTX_REG48 0x060
+#define RXTX_REG49 0x062
+#define RXTX_REG50 0x064
+#define RXTX_REG51 0x066
+#define RXTX_REG52 0x068
+#define RXTX_REG53 0x06a
+#define RXTX_REG54 0x06c
+#define RXTX_REG55 0x06e
+#define RXTX_REG61 0x07a
+#define RXTX_REG61_ISCAN_INBERT_SET(dst, src) \
+ (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define RXTX_REG61_LOADFREQ_SHIFT_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(dst, src) \
+ (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define RXTX_REG61_SPD_SEL_CDR_SET(dst, src) \
+ (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define RXTX_REG62 0x07c
+#define RXTX_REG62_PERIOD_H1_QLATCH_SET(dst, src) \
+ (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG81 0x0a2
+#define RXTX_REG89_MU_TH7_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG89_MU_TH8_SET(dst, src) \
+ (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define RXTX_REG89_MU_TH9_SET(dst, src) \
+ (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG96 0x0c0
+#define RXTX_REG96_MU_FREQ1_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG96_MU_FREQ2_SET(dst, src) \
+ (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define RXTX_REG96_MU_FREQ3_SET(dst, src) \
+ (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG99 0x0c6
+#define RXTX_REG99_MU_PHASE1_SET(dst, src) \
+ (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG99_MU_PHASE2_SET(dst, src) \
+ (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define RXTX_REG99_MU_PHASE3_SET(dst, src) \
+ (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG102 0x0cc
+#define RXTX_REG102_FREQLOOP_LIMIT_SET(dst, src) \
+ (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define RXTX_REG114 0x0e4
+#define RXTX_REG121 0x0f2
+#define RXTX_REG121_SUMOS_CAL_CODE_RD(src) ((0x0000003e & (u32)(src)) >> 0x1)
+#define RXTX_REG125 0x0fa
+#define RXTX_REG125_PQ_REG_SET(dst, src) \
+ (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define RXTX_REG125_SIGN_PQ_SET(dst, src) \
+ (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG125_SIGN_PQ_2C_SET(dst, src) \
+ (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define RXTX_REG125_PHZ_MANUALCODE_SET(dst, src) \
+ (((dst) & ~0x0000007c) | (((u32) (src) << 2) & 0x0000007c))
+#define RXTX_REG125_PHZ_MANUAL_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG127 0x0fe
+#define RXTX_REG127_FORCE_SUM_CAL_START_MASK 0x00000002
+#define RXTX_REG127_FORCE_LAT_CAL_START_MASK 0x00000004
+#define RXTX_REG127_FORCE_SUM_CAL_START_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG127_FORCE_LAT_CAL_START_SET(dst, src) \
+ (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define RXTX_REG127_LATCH_MAN_CAL_ENA_SET(dst, src) \
+ (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG127_DO_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define RXTX_REG127_XO_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG128 0x100
+#define RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(dst, src) \
+ (((dst) & ~0x0000000c) | (((u32) (src) << 2) & 0x0000000c))
+#define RXTX_REG128_EO_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define RXTX_REG128_SO_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG129 0x102
+#define RXTX_REG129_DE_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define RXTX_REG129_XE_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG130 0x104
+#define RXTX_REG130_EE_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define RXTX_REG130_SE_LATCH_MANCAL_SET(dst, src) \
+ (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG145 0x122
+#define RXTX_REG145_TX_IDLE_SATA_SET(dst, src) \
+ (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define RXTX_REG145_RXES_ENA_SET(dst, src) \
+ (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG145_RXDFE_CONFIG_SET(dst, src) \
+ (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define RXTX_REG145_RXVWES_LATENA_SET(dst, src) \
+ (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define RXTX_REG147 0x126
+#define RXTX_REG148 0x128
+
+/* Clock macro type */
+enum cmu_type_t {
+ REF_CMU = 0, /* Clock macro is the internal reference clock */
+ PHY_CMU = 1, /* Clock macro is the PLL for the Serdes */
+};
+
+enum mux_type_t {
+ MUX_SELECT_ATA = 0, /* Switch the MUX to ATA */
+ MUX_SELECT_SGMMII = 0, /* Switch the MUX to SGMII */
+};
+
+enum clk_type_t {
+ CLK_EXT_DIFF = 0, /* External differential */
+ CLK_INT_DIFF = 1, /* Internal differential */
+ CLK_INT_SING = 2, /* Internal single ended */
+};
+
+enum phy_mode {
+ MODE_SATA = 0, /* List them for simple reference */
+ MODE_SGMII = 1,
+ MODE_PCIE = 2,
+ MODE_USB = 3,
+ MODE_XFI = 4,
+ MODE_MAX
+};
+
+struct xgene_sata_override_param {
+ u32 speed[MAX_LANE]; /* Index for override parameter per lane */
+ u32 txspeed[3]; /* Tx speed */
+ u32 txboostgain[MAX_LANE*3]; /* Tx freq boost and gain control */
+ u32 txeyetuning[MAX_LANE*3]; /* Tx eye tuning */
+ u32 txeyedirection[MAX_LANE*3]; /* Tx eye tuning direction */
+ u32 txamplitude[MAX_LANE*3]; /* Tx amplitude control */
+ u32 txprecursor_cn1[MAX_LANE*3]; /* Tx emphasis taps 1st pre-cursor */
+ u32 txprecursor_cn2[MAX_LANE*3]; /* Tx emphasis taps 2nd pre-cursor */
+ u32 txpostcursor_cp1[MAX_LANE*3]; /* Tx emphasis taps post-cursor */
+};
+
+struct xgene_phy_ctx {
+ struct device *dev;
+ struct phy *phy;
+ enum phy_mode mode; /* Mode of operation */
+ enum clk_type_t clk_type; /* Input clock selection */
+ void __iomem *sds_base; /* PHY CSR base addr */
+ struct clk *clk; /* Optional clock */
+
+ /* Override Serdes parameters */
+ struct xgene_sata_override_param sata_param;
+};
+
+/*
+ * For chip earlier than A3 version, enable this flag.
+ * To enable, pass boot argument phy_xgene.preA3Chip=1
+ */
+static int preA3Chip;
+MODULE_PARM_DESC(preA3Chip, "Enable pre-A3 chip support (1=enable 0=disable)");
+module_param_named(preA3Chip, preA3Chip, int, 0444);
+
+static void sds_wr(void __iomem *csr_base, u32 indirect_cmd_reg,
+ u32 indirect_data_reg, u32 addr, u32 data)
+{
+ unsigned long deadline = jiffies + HZ;
+ u32 val;
+ u32 cmd;
+
+ cmd = CFG_IND_WR_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+ cmd = CFG_IND_ADDR_SET(cmd, addr);
+ writel(data, csr_base + indirect_data_reg);
+ readl(csr_base + indirect_data_reg); /* Force a barrier */
+ writel(cmd, csr_base + indirect_cmd_reg);
+ readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+ do {
+ val = readl(csr_base + indirect_cmd_reg);
+ } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+ time_before(jiffies, deadline));
+ if (!(val & CFG_IND_CMD_DONE_MASK))
+ pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+ csr_base + indirect_cmd_reg, addr, data);
+}
+
+static void sds_rd(void __iomem *csr_base, u32 indirect_cmd_reg,
+ u32 indirect_data_reg, u32 addr, u32 *data)
+{
+ unsigned long deadline = jiffies + HZ;
+ u32 val;
+ u32 cmd;
+
+ cmd = CFG_IND_RD_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+ cmd = CFG_IND_ADDR_SET(cmd, addr);
+ writel(cmd, csr_base + indirect_cmd_reg);
+ readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+ do {
+ val = readl(csr_base + indirect_cmd_reg);
+ } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+ time_before(jiffies, deadline));
+ *data = readl(csr_base + indirect_data_reg);
+ if (!(val & CFG_IND_CMD_DONE_MASK))
+ pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+ csr_base + indirect_cmd_reg, addr, *data);
+}
+
+static void cmu_wr(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+ u32 reg, u32 data)
+{
+ void __iomem *sds_base = ctx->sds_base;
+ u32 val;
+
+ if (cmu_type == REF_CMU)
+ reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+ else
+ reg += SERDES_PLL_INDIRECT_OFFSET;
+ sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+ sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+ pr_debug("CMU WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data, val);
+}
+
+static void cmu_rd(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+ u32 reg, u32 *data)
+{
+ void __iomem *sds_base = ctx->sds_base;
+
+ if (cmu_type == REF_CMU)
+ reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+ else
+ reg += SERDES_PLL_INDIRECT_OFFSET;
+ sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+ pr_debug("CMU RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void cmu_toggle1to0(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+ u32 reg, u32 bits)
+{
+ u32 val;
+
+ cmu_rd(ctx, cmu_type, reg, &val);
+ val |= bits;
+ cmu_wr(ctx, cmu_type, reg, val);
+ cmu_rd(ctx, cmu_type, reg, &val);
+ val &= ~bits;
+ cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_clrbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+ u32 reg, u32 bits)
+{
+ u32 val;
+
+ cmu_rd(ctx, cmu_type, reg, &val);
+ val &= ~bits;
+ cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_setbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+ u32 reg, u32 bits)
+{
+ u32 val;
+
+ cmu_rd(ctx, cmu_type, reg, &val);
+ val |= bits;
+ cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void serdes_wr(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 data)
+{
+ void __iomem *sds_base = ctx->sds_base;
+ u32 val;
+
+ reg += SERDES_INDIRECT_OFFSET;
+ reg += lane * SERDES_LANE_STRIDE;
+ sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+ sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+ pr_debug("SERDES WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data,
+ val);
+}
+
+static void serdes_rd(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 *data)
+{
+ void __iomem *sds_base = ctx->sds_base;
+
+ reg += SERDES_INDIRECT_OFFSET;
+ reg += lane * SERDES_LANE_STRIDE;
+ sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+ SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+ pr_debug("SERDES RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void serdes_clrbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+ u32 bits)
+{
+ u32 val;
+
+ serdes_rd(ctx, lane, reg, &val);
+ val &= ~bits;
+ serdes_wr(ctx, lane, reg, val);
+}
+
+static void serdes_setbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+ u32 bits)
+{
+ u32 val;
+
+ serdes_rd(ctx, lane, reg, &val);
+ val |= bits;
+ serdes_wr(ctx, lane, reg, val);
+}
+
+static void xgene_phy_cfg_cmu_clk_type(struct xgene_phy_ctx *ctx,
+ enum cmu_type_t cmu_type,
+ enum clk_type_t clk_type)
+{
+ u32 val;
+
+ /* Set the reset sequence delay for TX ready assertion */
+ cmu_rd(ctx, cmu_type, CMU_REG12, &val);
+ val = CMU_REG12_STATE_DELAY9_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG12, val);
+ /* Set the programmable stage delays between various enable stages */
+ cmu_wr(ctx, cmu_type, CMU_REG13, 0x0222);
+ cmu_wr(ctx, cmu_type, CMU_REG14, 0x2225);
+
+ /* Configure clock type */
+ if (clk_type == CLK_EXT_DIFF) {
+ /* Select external clock mux */
+ cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+ val = CMU_REG0_PLL_REF_SEL_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG0, val);
+ /* Select CMOS as reference clock */
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+ dev_dbg(ctx->dev, "Set external reference clock\n");
+ } else if (clk_type == CLK_INT_DIFF) {
+ /* Select internal clock mux */
+ cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+ val = CMU_REG0_PLL_REF_SEL_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG0, val);
+ /* Select CMOS as reference clock */
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+ dev_dbg(ctx->dev, "Set internal reference clock\n");
+ } else if (clk_type == CLK_INT_SING) {
+ /*
+ * NOTE: This clock type is NOT support for controller
+ * whose internal clock shared in the PCIe controller
+ *
+ * Select internal clock mux
+ */
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+ /* Select CML as reference clock */
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+ dev_dbg(ctx->dev,
+ "Set internal single ended reference clock\n");
+ }
+}
+
+static void xgene_phy_sata_cfg_cmu_core(struct xgene_phy_ctx *ctx,
+ enum cmu_type_t cmu_type,
+ enum clk_type_t clk_type)
+{
+ u32 val;
+ int ref_100MHz;
+
+ if (cmu_type == REF_CMU) {
+ /* Set VCO calibration voltage threshold */
+ cmu_rd(ctx, cmu_type, CMU_REG34, &val);
+ val = CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(val, 0x7);
+ val = CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(val, 0xc);
+ val = CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(val, 0x3);
+ val = CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(val, 0x8);
+ cmu_wr(ctx, cmu_type, CMU_REG34, val);
+ }
+
+ /* Set the VCO calibration counter */
+ cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+ if (cmu_type == REF_CMU || preA3Chip)
+ val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x4);
+ else
+ val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x7);
+ cmu_wr(ctx, cmu_type, CMU_REG0, val);
+
+ /* Configure PLL for calibration */
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_PLL_CP_SET(val, 0x1);
+ if (cmu_type == REF_CMU || preA3Chip)
+ val = CMU_REG1_PLL_CP_SEL_SET(val, 0x5);
+ else
+ val = CMU_REG1_PLL_CP_SEL_SET(val, 0x3);
+ if (cmu_type == REF_CMU)
+ val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+ else
+ val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+
+ if (cmu_type != REF_CMU)
+ cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+ /* Configure the PLL for either 100MHz or 50MHz */
+ cmu_rd(ctx, cmu_type, CMU_REG2, &val);
+ if (cmu_type == REF_CMU) {
+ val = CMU_REG2_PLL_LFRES_SET(val, 0xa);
+ ref_100MHz = 1;
+ } else {
+ val = CMU_REG2_PLL_LFRES_SET(val, 0x3);
+ if (clk_type == CLK_EXT_DIFF)
+ ref_100MHz = 0;
+ else
+ ref_100MHz = 1;
+ }
+ if (ref_100MHz) {
+ val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_100M);
+ val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_100M);
+ } else {
+ val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_50M);
+ val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_50M);
+ }
+ cmu_wr(ctx, cmu_type, CMU_REG2, val);
+
+ /* Configure the VCO */
+ cmu_rd(ctx, cmu_type, CMU_REG3, &val);
+ if (cmu_type == REF_CMU) {
+ val = CMU_REG3_VCOVARSEL_SET(val, 0x3);
+ val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x10);
+ } else {
+ val = CMU_REG3_VCOVARSEL_SET(val, 0xF);
+ if (preA3Chip)
+ val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x15);
+ else
+ val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x1a);
+ val = CMU_REG3_VCO_MANMOMSEL_SET(val, 0x15);
+ }
+ cmu_wr(ctx, cmu_type, CMU_REG3, val);
+
+ /* Disable force PLL lock */
+ cmu_rd(ctx, cmu_type, CMU_REG26, &val);
+ val = CMU_REG26_FORCE_PLL_LOCK_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG26, val);
+
+ /* Setup PLL loop filter */
+ cmu_rd(ctx, cmu_type, CMU_REG5, &val);
+ val = CMU_REG5_PLL_LFSMCAP_SET(val, 0x3);
+ val = CMU_REG5_PLL_LFCAP_SET(val, 0x3);
+ if (cmu_type == REF_CMU || !preA3Chip)
+ val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x7);
+ else
+ val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x4);
+ cmu_wr(ctx, cmu_type, CMU_REG5, val);
+
+ /* Enable or disable manual calibration */
+ cmu_rd(ctx, cmu_type, CMU_REG6, &val);
+ val = CMU_REG6_PLL_VREGTRIM_SET(val, preA3Chip ? 0x0 : 0x2);
+ val = CMU_REG6_MAN_PVT_CAL_SET(val, preA3Chip ? 0x1 : 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG6, val);
+
+ /* Configure lane for 20-bits */
+ if (cmu_type == PHY_CMU) {
+ cmu_rd(ctx, cmu_type, CMU_REG9, &val);
+ val = CMU_REG9_TX_WORD_MODE_CH1_SET(val,
+ CMU_REG9_WORD_LEN_20BIT);
+ val = CMU_REG9_TX_WORD_MODE_CH0_SET(val,
+ CMU_REG9_WORD_LEN_20BIT);
+ val = CMU_REG9_PLL_POST_DIVBY2_SET(val, 0x1);
+ if (!preA3Chip) {
+ val = CMU_REG9_VBG_BYPASSB_SET(val, 0x0);
+ val = CMU_REG9_IGEN_BYPASS_SET(val , 0x0);
+ }
+ cmu_wr(ctx, cmu_type, CMU_REG9, val);
+
+ if (!preA3Chip) {
+ cmu_rd(ctx, cmu_type, CMU_REG10, &val);
+ val = CMU_REG10_VREG_REFSEL_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG10, val);
+ }
+ }
+
+ cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+ val = CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(val, 0x1);
+ val = CMU_REG16_BYPASS_PLL_LOCK_SET(val, 0x1);
+ if (cmu_type == REF_CMU || preA3Chip)
+ val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x4);
+ else
+ val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+ cmu_wr(ctx, cmu_type, CMU_REG16, val);
+
+ /* Configure for SATA */
+ cmu_rd(ctx, cmu_type, CMU_REG30, &val);
+ val = CMU_REG30_PCIE_MODE_SET(val, 0x0);
+ val = CMU_REG30_LOCK_COUNT_SET(val, 0x3);
+ cmu_wr(ctx, cmu_type, CMU_REG30, val);
+
+ /* Disable state machine bypass */
+ cmu_wr(ctx, cmu_type, CMU_REG31, 0xF);
+
+ cmu_rd(ctx, cmu_type, CMU_REG32, &val);
+ val = CMU_REG32_PVT_CAL_WAIT_SEL_SET(val, 0x3);
+ if (cmu_type == REF_CMU || preA3Chip)
+ val = CMU_REG32_IREF_ADJ_SET(val, 0x3);
+ else
+ val = CMU_REG32_IREF_ADJ_SET(val, 0x1);
+ cmu_wr(ctx, cmu_type, CMU_REG32, val);
+
+ /* Set VCO calibration threshold */
+ if (cmu_type != REF_CMU && preA3Chip)
+ cmu_wr(ctx, cmu_type, CMU_REG34, 0x8d27);
+ else
+ cmu_wr(ctx, cmu_type, CMU_REG34, 0x873c);
+
+ /* Set CTLE Override and override waiting from state machine */
+ cmu_wr(ctx, cmu_type, CMU_REG37, 0xF00F);
+}
+
+static void xgene_phy_ssc_enable(struct xgene_phy_ctx *ctx,
+ enum cmu_type_t cmu_type)
+{
+ u32 val;
+
+ /* Set SSC modulation value */
+ cmu_rd(ctx, cmu_type, CMU_REG35, &val);
+ val = CMU_REG35_PLL_SSC_MOD_SET(val, 98);
+ cmu_wr(ctx, cmu_type, CMU_REG35, val);
+
+ /* Enable SSC, set vertical step and DSM value */
+ cmu_rd(ctx, cmu_type, CMU_REG36, &val);
+ val = CMU_REG36_PLL_SSC_VSTEP_SET(val, 30);
+ val = CMU_REG36_PLL_SSC_EN_SET(val, 1);
+ val = CMU_REG36_PLL_SSC_DSMSEL_SET(val, 1);
+ cmu_wr(ctx, cmu_type, CMU_REG36, val);
+
+ /* Reset the PLL */
+ cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+ cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+ /* Force VCO calibration to restart */
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+ CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx)
+{
+ u32 val;
+ u32 reg;
+ int i;
+ int lane;
+
+ for (lane = 0; lane < MAX_LANE; lane++) {
+ serdes_wr(ctx, lane, RXTX_REG147, 0x6);
+
+ /* Set boost control for quarter, half, and full rate */
+ serdes_rd(ctx, lane, RXTX_REG0, &val);
+ val = RXTX_REG0_CTLE_EQ_HR_SET(val, 0x10);
+ val = RXTX_REG0_CTLE_EQ_QR_SET(val, 0x10);
+ val = RXTX_REG0_CTLE_EQ_FR_SET(val, 0x10);
+ serdes_wr(ctx, lane, RXTX_REG0, val);
+
+ /* Set boost control value */
+ serdes_rd(ctx, lane, RXTX_REG1, &val);
+ val = RXTX_REG1_RXACVCM_SET(val, 0x7);
+ val = RXTX_REG1_CTLE_EQ_SET(val,
+ ctx->sata_param.txboostgain[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG1, val);
+
+ /* Latch VTT value based on the termination to ground and
+ enable TX FIFO */
+ serdes_rd(ctx, lane, RXTX_REG2, &val);
+ val = RXTX_REG2_VTT_ENA_SET(val, 0x1);
+ val = RXTX_REG2_VTT_SEL_SET(val, 0x1);
+ val = RXTX_REG2_TX_FIFO_ENA_SET(val, 0x1);
+ serdes_wr(ctx, lane, RXTX_REG2, val);
+
+ /* Configure Tx for 20-bits */
+ serdes_rd(ctx, lane, RXTX_REG4, &val);
+ val = RXTX_REG4_TX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+ serdes_wr(ctx, lane, RXTX_REG4, val);
+
+ if (!preA3Chip) {
+ serdes_rd(ctx, lane, RXTX_REG1, &val);
+ val = RXTX_REG1_RXVREG1_SET(val, 0x2);
+ val = RXTX_REG1_RXIREF_ADJ_SET(val, 0x2);
+ serdes_wr(ctx, lane, RXTX_REG1, val);
+ }
+
+ /* Set pre-emphasis first 1 and 2, and post-emphasis values */
+ serdes_rd(ctx, lane, RXTX_REG5, &val);
+ val = RXTX_REG5_TX_CN1_SET(val,
+ ctx->sata_param.txprecursor_cn1[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ val = RXTX_REG5_TX_CP1_SET(val,
+ ctx->sata_param.txpostcursor_cp1[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ val = RXTX_REG5_TX_CN2_SET(val,
+ ctx->sata_param.txprecursor_cn2[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG5, val);
+
+ /* Set TX amplitude value */
+ serdes_rd(ctx, lane, RXTX_REG6, &val);
+ val = RXTX_REG6_TXAMP_CNTL_SET(val,
+ ctx->sata_param.txamplitude[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ val = RXTX_REG6_TXAMP_ENA_SET(val, 0x1);
+ val = RXTX_REG6_TX_IDLE_SET(val, 0x0);
+ val = RXTX_REG6_RX_BIST_RESYNC_SET(val, 0x0);
+ val = RXTX_REG6_RX_BIST_ERRCNT_RD_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG6, val);
+
+ /* Configure Rx for 20-bits */
+ serdes_rd(ctx, lane, RXTX_REG7, &val);
+ val = RXTX_REG7_BIST_ENA_RX_SET(val, 0x0);
+ val = RXTX_REG7_RX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+ serdes_wr(ctx, lane, RXTX_REG7, val);
+
+ /* Set CDR and LOS values and enable Rx SSC */
+ serdes_rd(ctx, lane, RXTX_REG8, &val);
+ val = RXTX_REG8_CDR_LOOP_ENA_SET(val, 0x1);
+ val = RXTX_REG8_CDR_BYPASS_RXLOS_SET(val, 0x0);
+ val = RXTX_REG8_SSC_ENABLE_SET(val, 0x1);
+ val = RXTX_REG8_SD_DISABLE_SET(val, 0x0);
+ val = RXTX_REG8_SD_VREF_SET(val, 0x4);
+ serdes_wr(ctx, lane, RXTX_REG8, val);
+
+ /* Set phase adjust upper/lower limits */
+ serdes_rd(ctx, lane, RXTX_REG11, &val);
+ val = RXTX_REG11_PHASE_ADJUST_LIMIT_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG11, val);
+
+ /* Enable Latch Off; disable SUMOS and Tx termination */
+ serdes_rd(ctx, lane, RXTX_REG12, &val);
+ val = RXTX_REG12_LATCH_OFF_ENA_SET(val, 0x1);
+ val = RXTX_REG12_SUMOS_ENABLE_SET(val, 0x0);
+ val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG12, val);
+
+ /* Set period error latch to 512T and enable BWL */
+ serdes_rd(ctx, lane, RXTX_REG26, &val);
+ val = RXTX_REG26_PERIOD_ERROR_LATCH_SET(val, 0x0);
+ val = RXTX_REG26_BLWC_ENA_SET(val, 0x1);
+ serdes_wr(ctx, lane, RXTX_REG26, val);
+
+ serdes_wr(ctx, lane, RXTX_REG28, 0x0);
+
+ /* Set DFE loop preset value */
+ serdes_wr(ctx, lane, RXTX_REG31, 0x0);
+
+ /* Set Eye Monitor counter width to 12-bit */
+ serdes_rd(ctx, lane, RXTX_REG61, &val);
+ val = RXTX_REG61_ISCAN_INBERT_SET(val, 0x1);
+ val = RXTX_REG61_LOADFREQ_SHIFT_SET(val, 0x0);
+ val = RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG61, val);
+
+ serdes_rd(ctx, lane, RXTX_REG62, &val);
+ val = RXTX_REG62_PERIOD_H1_QLATCH_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG62, val);
+
+ /* Set BW select tap X for DFE loop */
+ for (i = 0; i < 9; i++) {
+ reg = RXTX_REG81 + i * 2;
+ serdes_rd(ctx, lane, reg, &val);
+ val = RXTX_REG89_MU_TH7_SET(val, 0xe);
+ val = RXTX_REG89_MU_TH8_SET(val, 0xe);
+ val = RXTX_REG89_MU_TH9_SET(val, 0xe);
+ serdes_wr(ctx, lane, reg, val);
+ }
+
+ /* Set BW select tap X for frequency adjust loop */
+ for (i = 0; i < 3; i++) {
+ reg = RXTX_REG96 + i * 2;
+ serdes_rd(ctx, lane, reg, &val);
+ val = RXTX_REG96_MU_FREQ1_SET(val, 0x10);
+ val = RXTX_REG96_MU_FREQ2_SET(val, 0x10);
+ val = RXTX_REG96_MU_FREQ3_SET(val, 0x10);
+ serdes_wr(ctx, lane, reg, val);
+ }
+
+ /* Set BW select tap X for phase adjust loop */
+ for (i = 0; i < 3; i++) {
+ reg = RXTX_REG99 + i * 2;
+ serdes_rd(ctx, lane, reg, &val);
+ val = RXTX_REG99_MU_PHASE1_SET(val, 0x7);
+ val = RXTX_REG99_MU_PHASE2_SET(val, 0x7);
+ val = RXTX_REG99_MU_PHASE3_SET(val, 0x7);
+ serdes_wr(ctx, lane, reg, val);
+ }
+
+ serdes_rd(ctx, lane, RXTX_REG102, &val);
+ val = RXTX_REG102_FREQLOOP_LIMIT_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG102, val);
+
+ serdes_wr(ctx, lane, RXTX_REG114, 0xffe0);
+
+ serdes_rd(ctx, lane, RXTX_REG125, &val);
+ val = RXTX_REG125_SIGN_PQ_SET(val,
+ ctx->sata_param.txeyedirection[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ val = RXTX_REG125_PQ_REG_SET(val,
+ ctx->sata_param.txeyetuning[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ val = RXTX_REG125_PHZ_MANUAL_SET(val, 0x1);
+ serdes_wr(ctx, lane, RXTX_REG125, val);
+
+ serdes_rd(ctx, lane, RXTX_REG127, &val);
+ val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x0);
+ serdes_wr(ctx, lane, RXTX_REG127, val);
+
+ serdes_rd(ctx, lane, RXTX_REG128, &val);
+ val = RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(val, 0x3);
+ serdes_wr(ctx, lane, RXTX_REG128, val);
+
+ serdes_rd(ctx, lane, RXTX_REG145, &val);
+ val = RXTX_REG145_RXDFE_CONFIG_SET(val, 0x3);
+ val = RXTX_REG145_TX_IDLE_SATA_SET(val, 0x0);
+ if (preA3Chip) {
+ val = RXTX_REG145_RXES_ENA_SET(val, 0x1);
+ val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x1);
+ } else {
+ val = RXTX_REG145_RXES_ENA_SET(val, 0x0);
+ val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x0);
+ }
+ serdes_wr(ctx, lane, RXTX_REG145, val);
+
+ /*
+ * Set Rx LOS filter clock rate, sample rate, and threshold
+ * windows
+ */
+ for (i = 0; i < 4; i++) {
+ reg = RXTX_REG148 + i * 2;
+ serdes_wr(ctx, lane, reg, 0xFFFF);
+ }
+ }
+}
+
+static int xgene_phy_cal_rdy_chk(struct xgene_phy_ctx *ctx,
+ enum cmu_type_t cmu_type,
+ enum clk_type_t clk_type)
+{
+ void __iomem *csr_serdes = ctx->sds_base;
+ int loop;
+ u32 val;
+
+ /* Release PHY main reset */
+ writel(0xdf, csr_serdes + SATA_ENET_SDS_RST_CTL);
+ readl(csr_serdes + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+
+ if (cmu_type != REF_CMU) {
+ cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+ /*
+ * As per PHY design spec, the PLL reset requires a minimum
+ * of 800us.
+ */
+ usleep_range(800, 1000);
+
+ cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+ val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG1, val);
+ /*
+ * As per PHY design spec, the PLL auto calibration requires
+ * a minimum of 800us.
+ */
+ usleep_range(800, 1000);
+
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+ CMU_REG32_FORCE_VCOCAL_START_MASK);
+ /*
+ * As per PHY design spec, the PLL requires a minimum of
+ * 800us to settle.
+ */
+ usleep_range(800, 1000);
+ }
+
+ if (!preA3Chip)
+ goto skip_manual_cal;
+
+ /*
+ * Configure the termination resister calibration
+ * The serial receive pins, RXP/RXN, have TERMination resistor
+ * that is required to be calibrated.
+ */
+ cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+ val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x12);
+ val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG17, val);
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG17,
+ CMU_REG17_PVT_TERM_MAN_ENA_MASK);
+ /*
+ * The serial transmit pins, TXP/TXN, have Pull-UP and Pull-DOWN
+ * resistors that are required to the calibrated.
+ * Configure the pull DOWN calibration
+ */
+ cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+ val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x29);
+ val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG17, val);
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+ CMU_REG16_PVT_DN_MAN_ENA_MASK);
+ /* Configure the pull UP calibration */
+ cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+ val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x28);
+ val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+ cmu_wr(ctx, cmu_type, CMU_REG17, val);
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+ CMU_REG16_PVT_UP_MAN_ENA_MASK);
+
+skip_manual_cal:
+ /* Poll the PLL calibration completion status for at least 1 ms */
+ loop = 100;
+ do {
+ cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+ if (CMU_REG7_PLL_CALIB_DONE_RD(val))
+ break;
+ /*
+ * As per PHY design spec, PLL calibration status requires
+ * a minimum of 10us to be updated.
+ */
+ usleep_range(10, 100);
+ } while (--loop > 0);
+
+ cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+ dev_dbg(ctx->dev, "PLL calibration %s\n",
+ CMU_REG7_PLL_CALIB_DONE_RD(val) ? "done" : "failed");
+ if (CMU_REG7_VCO_CAL_FAIL_RD(val)) {
+ dev_err(ctx->dev,
+ "PLL calibration failed due to VCO failure\n");
+ return -1;
+ }
+ dev_dbg(ctx->dev, "PLL calibration successful\n");
+
+ cmu_rd(ctx, cmu_type, CMU_REG15, &val);
+ dev_dbg(ctx->dev, "PHY Tx is %sready\n", val & 0x300 ? "" : "not ");
+ return 0;
+}
+
+static void xgene_phy_pdwn_force_vco(struct xgene_phy_ctx *ctx,
+ enum cmu_type_t cmu_type,
+ enum clk_type_t clk_type)
+{
+ u32 val;
+
+ dev_dbg(ctx->dev, "Reset VCO and re-start again\n");
+ if (cmu_type == PHY_CMU) {
+ cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+ val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+ cmu_wr(ctx, cmu_type, CMU_REG16, val);
+ }
+
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG0, CMU_REG0_PDOWN_MASK);
+ cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+ CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static int xgene_phy_hw_init_sata(struct xgene_phy_ctx *ctx,
+ enum clk_type_t clk_type, int ssc_enable)
+{
+ void __iomem *sds_base = ctx->sds_base;
+ u32 val;
+ int i;
+
+ /* Configure the PHY for operation */
+ dev_dbg(ctx->dev, "Reset PHY\n");
+ /* Place PHY into reset */
+ writel(0x0, sds_base + SATA_ENET_SDS_RST_CTL);
+ val = readl(sds_base + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+ /* Release PHY lane from reset (active high) */
+ writel(0x20, sds_base + SATA_ENET_SDS_RST_CTL);
+ readl(sds_base + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+ /* Release all PHY module out of reset except PHY main reset */
+ writel(0xde, sds_base + SATA_ENET_SDS_RST_CTL);
+ readl(sds_base + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+
+ /* Set the operation speed */
+ val = readl(sds_base + SATA_ENET_SDS_CTL1);
+ val = CFG_I_SPD_SEL_CDR_OVR1_SET(val,
+ ctx->sata_param.txspeed[ctx->sata_param.speed[0]]);
+ writel(val, sds_base + SATA_ENET_SDS_CTL1);
+
+ dev_dbg(ctx->dev, "Set the customer pin mode to SATA\n");
+ val = readl(sds_base + SATA_ENET_SDS_CTL0);
+ val = REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(val, 0x4421);
+ writel(val, sds_base + SATA_ENET_SDS_CTL0);
+
+ /* Configure the clock macro unit (CMU) clock type */
+ xgene_phy_cfg_cmu_clk_type(ctx, PHY_CMU, clk_type);
+
+ /* Configure the clock macro */
+ xgene_phy_sata_cfg_cmu_core(ctx, PHY_CMU, clk_type);
+
+ /* Enable SSC if enabled */
+ if (ssc_enable)
+ xgene_phy_ssc_enable(ctx, PHY_CMU);
+
+ /* Configure PHY lanes */
+ xgene_phy_sata_cfg_lanes(ctx);
+
+ /* Set Rx/Tx 20-bit */
+ val = readl(sds_base + SATA_ENET_SDS_PCS_CTL0);
+ val = REGSPEC_CFG_I_RX_WORDMODE0_SET(val, 0x3);
+ val = REGSPEC_CFG_I_TX_WORDMODE0_SET(val, 0x3);
+ writel(val, sds_base + SATA_ENET_SDS_PCS_CTL0);
+
+ /* Start PLL calibration and try for three times */
+ i = 10;
+ do {
+ if (!xgene_phy_cal_rdy_chk(ctx, PHY_CMU, clk_type))
+ break;
+ /* If failed, toggle the VCO power signal and start again */
+ xgene_phy_pdwn_force_vco(ctx, PHY_CMU, clk_type);
+ } while (--i > 0);
+ /* Even on failure, allow to continue any way */
+ if (i <= 0)
+ dev_err(ctx->dev, "PLL calibration failed\n");
+
+ return 0;
+}
+
+static int xgene_phy_hw_initialize(struct xgene_phy_ctx *ctx,
+ enum clk_type_t clk_type,
+ int ssc_enable)
+{
+ int rc;
+
+ dev_dbg(ctx->dev, "PHY init clk type %d\n", clk_type);
+
+ if (ctx->mode == MODE_SATA) {
+ rc = xgene_phy_hw_init_sata(ctx, clk_type, ssc_enable);
+ if (rc)
+ return rc;
+ } else {
+ dev_err(ctx->dev, "Un-supported customer pin mode %d\n",
+ ctx->mode);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Receiver Offset Calibration:
+ *
+ * Calibrate the receiver signal path offset in two steps - summar and
+ * latch calibrations
+ */
+static void xgene_phy_force_lat_summer_cal(struct xgene_phy_ctx *ctx, int lane)
+{
+ int i;
+ struct {
+ u32 reg;
+ u32 val;
+ } serdes_reg[] = {
+ {RXTX_REG38, 0x0},
+ {RXTX_REG39, 0xff00},
+ {RXTX_REG40, 0xffff},
+ {RXTX_REG41, 0xffff},
+ {RXTX_REG42, 0xffff},
+ {RXTX_REG43, 0xffff},
+ {RXTX_REG44, 0xffff},
+ {RXTX_REG45, 0xffff},
+ {RXTX_REG46, 0xffff},
+ {RXTX_REG47, 0xfffc},
+ {RXTX_REG48, 0x0},
+ {RXTX_REG49, 0x0},
+ {RXTX_REG50, 0x0},
+ {RXTX_REG51, 0x0},
+ {RXTX_REG52, 0x0},
+ {RXTX_REG53, 0x0},
+ {RXTX_REG54, 0x0},
+ {RXTX_REG55, 0x0},
+ };
+
+ /* Start SUMMER calibration */
+ serdes_setbits(ctx, lane, RXTX_REG127,
+ RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+ /*
+ * As per PHY design spec, the Summer calibration requires a minimum
+ * of 100us to complete.
+ */
+ usleep_range(100, 500);
+ serdes_clrbits(ctx, lane, RXTX_REG127,
+ RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+ /*
+ * As per PHY design spec, the auto calibration requires a minimum
+ * of 100us to complete.
+ */
+ usleep_range(100, 500);
+
+ /* Start latch calibration */
+ serdes_setbits(ctx, lane, RXTX_REG127,
+ RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+ /*
+ * As per PHY design spec, the latch calibration requires a minimum
+ * of 100us to complete.
+ */
+ usleep_range(100, 500);
+ serdes_clrbits(ctx, lane, RXTX_REG127,
+ RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+
+ /* Configure the PHY lane for calibration */
+ serdes_wr(ctx, lane, RXTX_REG28, 0x7);
+ serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+ serdes_clrbits(ctx, lane, RXTX_REG4,
+ RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK);
+ serdes_clrbits(ctx, lane, RXTX_REG7,
+ RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK);
+ for (i = 0; i < ARRAY_SIZE(serdes_reg); i++)
+ serdes_wr(ctx, lane, serdes_reg[i].reg,
+ serdes_reg[i].val);
+}
+
+static void xgene_phy_reset_rxd(struct xgene_phy_ctx *ctx, int lane)
+{
+ /* Reset digital Rx */
+ serdes_clrbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+ /* As per PHY design spec, the reset requires a minimum of 100us. */
+ usleep_range(100, 150);
+ serdes_setbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+}
+
+static int xgene_phy_get_avg(int accum, int samples)
+{
+ return (accum + (samples / 2)) / samples;
+}
+
+static void xgene_phy_gen_avg_val(struct xgene_phy_ctx *ctx, int lane)
+{
+ int max_loop = 10;
+ int avg_loop = 0;
+ int lat_do = 0, lat_xo = 0, lat_eo = 0, lat_so = 0;
+ int lat_de = 0, lat_xe = 0, lat_ee = 0, lat_se = 0;
+ int sum_cal = 0;
+ int lat_do_itr, lat_xo_itr, lat_eo_itr, lat_so_itr;
+ int lat_de_itr, lat_xe_itr, lat_ee_itr, lat_se_itr;
+ int sum_cal_itr;
+ int fail_even;
+ int fail_odd;
+ u32 val;
+
+ dev_dbg(ctx->dev, "Generating avg calibration value for lane %d\n",
+ lane);
+
+ /* Enable RX Hi-Z termination */
+ serdes_setbits(ctx, lane, RXTX_REG12,
+ RXTX_REG12_RX_DET_TERM_ENABLE_MASK);
+ /* Turn off DFE */
+ serdes_wr(ctx, lane, RXTX_REG28, 0x0000);
+ /* DFE Presets to zero */
+ serdes_wr(ctx, lane, RXTX_REG31, 0x0000);
+
+ /*
+ * Receiver Offset Calibration:
+ * Calibrate the receiver signal path offset in two steps - summar
+ * and latch calibration.
+ * Runs the "Receiver Offset Calibration multiple times to determine
+ * the average value to use.
+ */
+ while (avg_loop < max_loop) {
+ /* Start the calibration */
+ xgene_phy_force_lat_summer_cal(ctx, lane);
+
+ serdes_rd(ctx, lane, RXTX_REG21, &val);
+ lat_do_itr = RXTX_REG21_DO_LATCH_CALOUT_RD(val);
+ lat_xo_itr = RXTX_REG21_XO_LATCH_CALOUT_RD(val);
+ fail_odd = RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(val);
+
+ serdes_rd(ctx, lane, RXTX_REG22, &val);
+ lat_eo_itr = RXTX_REG22_EO_LATCH_CALOUT_RD(val);
+ lat_so_itr = RXTX_REG22_SO_LATCH_CALOUT_RD(val);
+ fail_even = RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(val);
+
+ serdes_rd(ctx, lane, RXTX_REG23, &val);
+ lat_de_itr = RXTX_REG23_DE_LATCH_CALOUT_RD(val);
+ lat_xe_itr = RXTX_REG23_XE_LATCH_CALOUT_RD(val);
+
+ serdes_rd(ctx, lane, RXTX_REG24, &val);
+ lat_ee_itr = RXTX_REG24_EE_LATCH_CALOUT_RD(val);
+ lat_se_itr = RXTX_REG24_SE_LATCH_CALOUT_RD(val);
+
+ serdes_rd(ctx, lane, RXTX_REG121, &val);
+ sum_cal_itr = RXTX_REG121_SUMOS_CAL_CODE_RD(val);
+
+ /* Check for failure. If passed, sum them for averaging */
+ if ((fail_even == 0 || fail_even == 1) &&
+ (fail_odd == 0 || fail_odd == 1)) {
+ lat_do += lat_do_itr;
+ lat_xo += lat_xo_itr;
+ lat_eo += lat_eo_itr;
+ lat_so += lat_so_itr;
+ lat_de += lat_de_itr;
+ lat_xe += lat_xe_itr;
+ lat_ee += lat_ee_itr;
+ lat_se += lat_se_itr;
+ sum_cal += sum_cal_itr;
+
+ dev_dbg(ctx->dev, "Iteration %d:\n", avg_loop);
+ dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+ lat_do_itr, lat_xo_itr, lat_eo_itr,
+ lat_so_itr);
+ dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+ lat_de_itr, lat_xe_itr, lat_ee_itr,
+ lat_se_itr);
+ dev_dbg(ctx->dev, "SUM 0x%x\n", sum_cal_itr);
+ ++avg_loop;
+ } else {
+ dev_err(ctx->dev,
+ "Receiver calibration failed at %d loop\n",
+ avg_loop);
+ }
+ xgene_phy_reset_rxd(ctx, lane);
+ }
+
+ /* Update latch manual calibration with average value */
+ serdes_rd(ctx, lane, RXTX_REG127, &val);
+ val = RXTX_REG127_DO_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_do, max_loop));
+ val = RXTX_REG127_XO_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_xo, max_loop));
+ serdes_wr(ctx, lane, RXTX_REG127, val);
+
+ serdes_rd(ctx, lane, RXTX_REG128, &val);
+ val = RXTX_REG128_EO_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_eo, max_loop));
+ val = RXTX_REG128_SO_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_so, max_loop));
+ serdes_wr(ctx, lane, RXTX_REG128, val);
+
+ serdes_rd(ctx, lane, RXTX_REG129, &val);
+ val = RXTX_REG129_DE_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_de, max_loop));
+ val = RXTX_REG129_XE_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_xe, max_loop));
+ serdes_wr(ctx, lane, RXTX_REG129, val);
+
+ serdes_rd(ctx, lane, RXTX_REG130, &val);
+ val = RXTX_REG130_EE_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_ee, max_loop));
+ val = RXTX_REG130_SE_LATCH_MANCAL_SET(val,
+ xgene_phy_get_avg(lat_se, max_loop));
+ serdes_wr(ctx, lane, RXTX_REG130, val);
+
+ /* Update SUMMER calibration with average value */
+ serdes_rd(ctx, lane, RXTX_REG14, &val);
+ val = RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(val,
+ xgene_phy_get_avg(sum_cal, max_loop));
+ serdes_wr(ctx, lane, RXTX_REG14, val);
+
+ dev_dbg(ctx->dev, "Average Value:\n");
+ dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+ xgene_phy_get_avg(lat_do, max_loop),
+ xgene_phy_get_avg(lat_xo, max_loop),
+ xgene_phy_get_avg(lat_eo, max_loop),
+ xgene_phy_get_avg(lat_so, max_loop));
+ dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+ xgene_phy_get_avg(lat_de, max_loop),
+ xgene_phy_get_avg(lat_xe, max_loop),
+ xgene_phy_get_avg(lat_ee, max_loop),
+ xgene_phy_get_avg(lat_se, max_loop));
+ dev_dbg(ctx->dev, "SUM 0x%x\n",
+ xgene_phy_get_avg(sum_cal, max_loop));
+
+ serdes_rd(ctx, lane, RXTX_REG14, &val);
+ val = RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(val, 0x1);
+ serdes_wr(ctx, lane, RXTX_REG14, val);
+ dev_dbg(ctx->dev, "Enable Manual Summer calibration\n");
+
+ serdes_rd(ctx, lane, RXTX_REG127, &val);
+ val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x1);
+ dev_dbg(ctx->dev, "Enable Manual Latch calibration\n");
+ serdes_wr(ctx, lane, RXTX_REG127, val);
+
+ /* Disable RX Hi-Z termination */
+ serdes_rd(ctx, lane, RXTX_REG12, &val);
+ val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0);
+ serdes_wr(ctx, lane, RXTX_REG12, val);
+ /* Turn on DFE */
+ serdes_wr(ctx, lane, RXTX_REG28, 0x0007);
+ /* Set DFE preset */
+ serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+}
+
+static int xgene_phy_hw_init(struct phy *phy)
+{
+ struct xgene_phy_ctx *ctx = phy_get_drvdata(phy);
+ int rc;
+ int i;
+
+ rc = xgene_phy_hw_initialize(ctx, CLK_EXT_DIFF, SSC_DISABLE);
+ if (rc) {
+ dev_err(ctx->dev, "PHY initialize failed %d\n", rc);
+ return rc;
+ }
+
+ /* Setup clock properly after PHY configuration */
+ if (!IS_ERR(ctx->clk)) {
+ /* HW requires an toggle */
+ clk_prepare_enable(ctx->clk);
+ clk_disable_unprepare(ctx->clk);
+ clk_prepare_enable(ctx->clk);
+ }
+
+ /* Compute average value */
+ for (i = 0; i < MAX_LANE; i++)
+ xgene_phy_gen_avg_val(ctx, i);
+
+ dev_dbg(ctx->dev, "PHY initialized\n");
+ return 0;
+}
+
+/*
+ * This function is used to configure the PHY to operation as either SATA Gen1
+ * or Gen2 speed.
+ */
+static void xgene_phy_sata_force_gen(struct xgene_phy_ctx *ctx,
+ int lane, int gen)
+{
+ u32 val;
+
+ serdes_rd(ctx, lane, RXTX_REG38, &val);
+ val = RXTX_REG38_CUSTOMER_PINMODE_INV_SET(val, 0x400);
+ serdes_wr(ctx, lane, RXTX_REG38, val);
+
+ /* Set boost control value */
+ serdes_rd(ctx, lane, RXTX_REG1, &val);
+ val = RXTX_REG1_RXACVCM_SET(val, 0x7);
+ val = RXTX_REG1_CTLE_EQ_SET(val,
+ ctx->sata_param.txboostgain[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG1, val);
+
+ serdes_rd(ctx, lane, RXTX_REG125, &val);
+ val = RXTX_REG125_PQ_REG_SET(val,
+ ctx->sata_param.txeyetuning[lane * 3 +
+ ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG125, val);
+
+ serdes_rd(ctx, lane, RXTX_REG61, &val);
+ val = RXTX_REG61_SPD_SEL_CDR_SET(val,
+ ctx->sata_param.txspeed[ctx->sata_param.speed[lane]]);
+ serdes_wr(ctx, lane, RXTX_REG61, val);
+}
+
+static int xgene_phy_set_speed(struct phy *phy, int lane, u64 speed)
+{
+ struct xgene_phy_ctx *ctx = phy_get_drvdata(phy);
+
+ if (lane >= MAX_LANE)
+ return -EINVAL;
+ if (ctx->mode == MODE_SATA) {
+ if (speed >= 6000000000ULL /* 6Gbps */) {
+ ctx->sata_param.speed[lane] = 2;
+ xgene_phy_sata_force_gen(ctx, lane, SATA_SPD_SEL_GEN3);
+ } else if (speed >= 3000000000ULL /* 3Gbps */) {
+ ctx->sata_param.speed[lane] = 1;
+ xgene_phy_sata_force_gen(ctx, lane, SATA_SPD_SEL_GEN2);
+ } else if (speed >= 1500000000ULL /* 1.5Gbps */) {
+ ctx->sata_param.speed[lane] = 0;
+ xgene_phy_sata_force_gen(ctx, lane, SATA_SPD_SEL_GEN1);
+ } else if (speed == 0) {
+ xgene_phy_reset_rxd(ctx, lane);
+ }
+ }
+ return 0;
+}
+
+static const struct phy_ops xgene_phy_ops = {
+ .init = xgene_phy_hw_init,
+ .set_speed = xgene_phy_set_speed,
+ .owner = THIS_MODULE,
+};
+
+static struct phy *xgene_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct xgene_phy_ctx *ctx = dev_get_drvdata(dev);
+
+ if (args->args_count > 0) {
+ if (args->args[0] >= MODE_MAX)
+ return NULL;
+ ctx->mode = args->args[0];
+ }
+ return ctx->phy;
+}
+
+static void xgene_phy_get_param(struct platform_device *pdev,
+ const char *name, u32 *buffer,
+ int count, u32 *default_val,
+ u32 conv_factor)
+{
+ int i;
+
+ if (!of_property_read_u32_array(pdev->dev.of_node, name, buffer,
+ count)) {
+ for (i = 0; i < count; i++)
+ buffer[i] /= conv_factor;
+ return;
+ }
+ /* Does not exist, load default */
+ for (i = 0; i < count; i++)
+ buffer[i] = default_val[i % 3];
+}
+
+static int xgene_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct xgene_phy_ctx *ctx;
+ struct resource *res;
+ int rc = 0;
+ u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
+ u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
+ u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
+ u32 default_txeye_tuning[] = DEFAULT_SATA_TXEYETUNING;
+ u32 default_txamp[] = DEFAULT_SATA_TXAMP;
+ u32 default_txcn1[] = DEFAULT_SATA_TXCN1;
+ u32 default_txcn2[] = DEFAULT_SATA_TXCN2;
+ u32 default_txcp1[] = DEFAULT_SATA_TXCP1;
+ int i;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(&pdev->dev, "can't allocate PHY context\n");
+ return -ENOMEM;
+ }
+ ctx->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no PHY resource address\n");
+ rc = -EINVAL;
+ goto error;
+ }
+ ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
+ if (!ctx->sds_base) {
+ dev_err(&pdev->dev, "can't map PHY resource\n");
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ /* Retrieve optional clock */
+ ctx->clk = clk_get(&pdev->dev, NULL);
+
+ /* Load override paramaters */
+ xgene_phy_get_param(pdev, "apm,tx-eye-tuning",
+ ctx->sata_param.txeyetuning, 6, default_txeye_tuning, 1);
+ xgene_phy_get_param(pdev, "apm,tx-eye-direction",
+ ctx->sata_param.txeyedirection, 6, default_txeye_direction, 1);
+ xgene_phy_get_param(pdev, "apm,tx-boost-gain",
+ ctx->sata_param.txboostgain, 6, default_txboost_gain, 1);
+ xgene_phy_get_param(pdev, "apm,tx-amplitude",
+ ctx->sata_param.txamplitude, 6, default_txamp, 13300);
+ xgene_phy_get_param(pdev, "apm,tx-pre-cursor1",
+ ctx->sata_param.txprecursor_cn1, 6, default_txcn1, 18200);
+ xgene_phy_get_param(pdev, "apm,tx-pre-cursor2",
+ ctx->sata_param.txprecursor_cn2, 6, default_txcn2, 18200);
+ xgene_phy_get_param(pdev, "apm,tx-post-cursor",
+ ctx->sata_param.txpostcursor_cp1, 6, default_txcp1, 18200);
+ xgene_phy_get_param(pdev, "apm,tx-speed",
+ ctx->sata_param.txspeed, 3, default_spd, 1);
+ for (i = 0; i < MAX_LANE; i++)
+ ctx->sata_param.speed[i] = 2; /* Default to Gen3 */
+
+ ctx->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ctx);
+
+ ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL);
+ if (IS_ERR(ctx->phy)) {
+ dev_dbg(&pdev->dev, "Failed to create PHY\n");
+ rc = PTR_ERR(ctx->phy);
+ goto error;
+ }
+ phy_set_drvdata(ctx->phy, ctx);
+
+ phy_provider = devm_of_phy_provider_register(ctx->dev,
+ xgene_phy_xlate);
+ if (IS_ERR(phy_provider)) {
+ rc = PTR_ERR(phy_provider);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ return rc;
+}
+
+static const struct of_device_id xgene_phy_of_match[] = {
+ {.compatible = "apm,xgene-phy",},
+ {},
+};
+MODULE_DEVICE_TABLE(of, xgene_phy_of_match);
+
+static struct platform_driver xgene_phy_driver = {
+ .probe = xgene_phy_probe,
+ .driver = {
+ .name = "xgene-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = xgene_phy_of_match,
+ },
+};
+module_platform_driver(xgene_phy_driver);
+
+MODULE_DESCRIPTION("APM X-Gene Multi-Purpose PHY driver");
+MODULE_AUTHOR("Loc Ho <lho@apm.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
--
1.5.5
^ permalink raw reply related
* [PATCH v12 4/4] arm64: Add APM X-Gene SoC 15Gbps Multi-purpose PHY DTS entries
From: Loc Ho @ 2014-02-27 18:14 UTC (permalink / raw)
To: kishon, tj
Cc: olof, arnd, linux-kernel, linux-scsi, linux-ide, devicetree,
linux-arm-kernel, ddutile, jcm, patches, Loc Ho, Tuan Phan,
Suman Tripathi
In-Reply-To: <1393524848-8207-4-git-send-email-lho@apm.com>
This patch adds the DTS entries for the APM X-Gene SoC 15Gbps Multi-purpose
PHY driver. The PHY for SATA controller 2 and 3 are enabled by default.
Signed-off-by: Loc Ho <lho@apm.com>
Signed-off-by: Tuan Phan <tphan@apm.com>
Signed-off-by: Suman Tripathi <stripathi@apm.com>
---
arch/arm64/boot/dts/apm-storm.dtsi | 75 ++++++++++++++++++++++++++++++++++++
1 files changed, 75 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi
index d37d736..c78ddcf 100644
--- a/arch/arm64/boot/dts/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm-storm.dtsi
@@ -176,6 +176,51 @@
reg-names = "csr-reg";
clock-output-names = "eth8clk";
};
+
+ sataphy1clk: sataphy1clk@1f21c000 {
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ clock-names = "socplldiv2";
+ reg = <0x0 0x1f21c000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "sataphy1clk";
+ status = "disabled";
+ csr-offset = <0x4>;
+ csr-mask = <0x00>;
+ enable-offset = <0x0>;
+ enable-mask = <0x06>;
+ };
+
+ sataphy2clk: sataphy1clk@1f22c000 {
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ clock-names = "socplldiv2";
+ reg = <0x0 0x1f22c000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "sataphy2clk";
+ status = "ok";
+ csr-offset = <0x4>;
+ csr-mask = <0x3a>;
+ enable-offset = <0x0>;
+ enable-mask = <0x06>;
+ };
+
+ sataphy3clk: sataphy1clk@1f23c000 {
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ clock-names = "socplldiv2";
+ reg = <0x0 0x1f23c000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "sataphy3clk";
+ status = "ok";
+ csr-offset = <0x4>;
+ csr-mask = <0x3a>;
+ enable-offset = <0x0>;
+ enable-mask = <0x06>;
+ };
};
serial0: serial@1c020000 {
@@ -187,5 +232,35 @@
interrupt-parent = <&gic>;
interrupts = <0x0 0x4c 0x4>;
};
+
+ phy1: phy@1f21a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f21a000 0x0 0x100>;
+ #phy-cells = <1>;
+ clocks = <&sataphy1clk 0>;
+ status = "disabled";
+ apm,tx-boost-gain = <30 30 30 30 30 30>;
+ apm,tx-eye-tuning = <2 10 10 2 10 10>;
+ };
+
+ phy2: phy@1f22a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f22a000 0x0 0x100>;
+ #phy-cells = <1>;
+ clocks = <&sataphy2clk 0>;
+ status = "ok";
+ apm,tx-boost-gain = <30 30 30 30 30 30>;
+ apm,tx-eye-tuning = <1 10 10 2 10 10>;
+ };
+
+ phy3: phy@1f23a000 {
+ compatible = "apm,xgene-phy";
+ reg = <0x0 0x1f23a000 0x0 0x100>;
+ #phy-cells = <1>;
+ clocks = <&sataphy3clk 0>;
+ status = "ok";
+ apm,tx-boost-gain = <31 31 31 31 31 31>;
+ apm,tx-eye-tuning = <2 10 10 2 10 10>;
+ };
};
};
--
1.5.5
^ permalink raw reply related
* Re: [PATCH v2 1/4] pci: OF: Fix the conversion of IO ranges into IO resources.
From: Jason Gunthorpe @ 2014-02-27 18:19 UTC (permalink / raw)
To: Liviu Dudau
Cc: linux-pci, Bjorn Helgaas, Catalin Marinas, Will Deacon,
linaro-kernel, devicetree@vger.kernel.org, LKML, LAKML
In-Reply-To: <1393506402-11474-2-git-send-email-Liviu.Dudau@arm.com>
On Thu, Feb 27, 2014 at 01:06:39PM +0000, Liviu Dudau wrote:
> + if (res->flags & IORESOURCE_IO) {
> + unsigned long port;
> + port = pci_address_to_pio(range->pci_addr);
This looks very suspicious, pci_addr is not unique across all domains,
so there is no way to convert from a pci_addr to the virtual IO
address without knowing the domain number as well.
I would like to see it be:
port = pci_address_to_pio(range->cpu_addr);
cpu_addr is unique across all domains.
Looking at the microblaze and PPC versions I think the above version
is actually correct (assuming io_base_phys is the CPU address of the
IO window)
Jason
^ permalink raw reply
* [PATCH 1/1] Memory leak in scripts/dtc/util.c
From: xypron.glpk-Mmb7MZpHnFY @ 2014-02-27 18:24 UTC (permalink / raw)
To: grant.likely-QSEj5FYQhm4dnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Heinrich Schuchardt
From: Heinrich Schuchardt <xypron.glpk-Mmb7MZpHnFY@public.gmane.org>
buf was leaked if out of memory
Signed-off-by: Heinrich Schuchardt <xypron.glpk-Mmb7MZpHnFY@public.gmane.org>
---
scripts/dtc/util.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/dtc/util.c b/scripts/dtc/util.c
index 2422c34..a1e2e59 100644
--- a/scripts/dtc/util.c
+++ b/scripts/dtc/util.c
@@ -210,9 +210,11 @@ int utilfdt_read_err(const char *filename, char **buffp)
do {
/* Expand the buffer to hold the next chunk */
if (offset == bufsize) {
+ char *buf_old = buf;
bufsize *= 2;
buf = realloc(buf, bufsize);
if (!buf) {
+ buf = buf_old;
ret = ENOMEM;
break;
}
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [RFC PATCH 1/6] PM / Voltagedomain: Add generic clk notifier handler for regulator based dynamic voltage scaling
From: Felipe Balbi @ 2014-02-27 18:59 UTC (permalink / raw)
To: Nishanth Menon
Cc: Mike Turquette, Rafael J. Wysocki, Viresh Kumar, MyungJoo Ham,
Mark Brown, devicetree, linux-doc, linux-kernel, cpufreq,
linux-pm, linux-arm-kernel, linux-omap, Kevin Hilman
In-Reply-To: <20140227023455.GA15712@kahuna>
[-- Attachment #1: Type: text/plain, Size: 7312 bytes --]
Hi,
On Wed, Feb 26, 2014 at 08:34:55PM -0600, Nishanth Menon wrote:
> On 14:56-20140225, Nishanth Menon wrote:
> > On 02/24/2014 11:51 PM, Mike Turquette wrote:
> > > Quoting Nishanth Menon (2014-02-18 12:32:18)
> [...]
> > > I'm not sure about trying to capture the "voltdm" as a core concept. It
> > > feels a bit unwieldy to me.
> >
> > Considering it is a simple collation of regulators and SoC specific
> > "magic" which have to be operated in tandem to clock operation, Why
> > does it seem unwieldy? Usage of multiple voltage planes in a single
> > voltage domain concept does not seem unique to TI processors either:
> > For example, imx6q-cpufreq.c uses 3 regulators (arm, pu, soc),
> > s5pv210-cpufreq.c uses two regulators (vddarm, vddint), ideally OMAP
> > implementation would use two (vdd_mpu, vbb_mpu).
> >
> > > I have wondered about making an abstract
> > > "performance domain" which is the dvfs analogue to generic power
> > > domains. This a reasonable split since gpd are good for idle power
> > > savings (e.g. clock gate, power gate, sleep state, etc) and "perf
> > > domains" would be good for active power savings (dvfs).
> > >
> > > Having a generic container for performance domains might make a good
> > > place to stuff all of this glue logic that we keep running into (e.g.
> > > CPU and GPU max frequencies that are related), and it might make another
> > > nice knob for the thermal folks to use.
> >
> > This sounds like one level higher abstraction that we are speaking of
> > here? I was'nt intending to solve the bigger picture problem here -
> > just an abstraction level that might allow reusablity for multiple
> > SoCs. In fact, having an abstraction away for voltage domain(which may
> > consist of multiple regulators and any SoC specific magic) purely
> > allows us to move towards a direction you mention here.
> >
> > >
> > > For the case of the OMAP voltage domains, it would be a place to stuff
> > > all of the VC/VP -> ABB -> Smart Reflex AVS stuff.
> > >
> >
> > Unfortunately, I dont completely comprehend objection we have to this
> > approach (other than an higher level abstraction is needed) and if we
> > do have an objection, what is the alternate approach should be for
> > representing hardware which this series attempts to present.
>
> I think the following is around the lines of your thought direction -
> if Rafael or others have comments on the following approach, it'd be a
> good starting point for me to progress.
>
> -->8--
> From 62e50b9f920495db88e5594aa6bceb52e83a443d Mon Sep 17 00:00:00 2001
> From: Nishanth Menon <nm@ti.com>
> Date: Wed, 26 Feb 2014 10:59:59 -0600
> Subject: [PATCH] PM / Runtime: introduce active power management callbacks
> for pm_domain
>
> dev_pm_domain currently handles just device idle power management
> using the generic pm_runtime_get|put and related family of functions.
>
> Logically with appropriate pm_domain hooks this can translate to
> hardware specific clock and related operations. Given that pm_domains
> may contain this information, this provides an opportunity to extend
> current pm_runtime do dynamic power operations as well.
>
> What this means for drivers is as follows:
>
> Today, drivers(with some level of complexity) do:
> pm_runtime_get_sync(dev);
> clk = clk_get(dev, "name");
> old_rate = clk_get_rate(clk);
> ...
> clk_set_rate(clk, new_rate);
> ...
> clk_put(clk);
> pm_runtime_get_sync(dev);
>
> Instead, on pm_domains that can handle this as part of
> pm_domain->active_ops functions, They can now do the following:
> pm_runtime_get_sync(dev);
> old_rate = pm_runtime_get_rate(dev);
> ...
> pm_runtime_set_rate(dev, new_rate);
> ...
> pm_runtime_put_sync(dev);
>
> Obviously, this'd work for devices that handle a single main
> functional clock, but this could reduce complexity of drivers having
> to deal with power management details to have pm_runtime as the main
> point of interface.
>
> CAVEAT: For power domains that are capable of handling multiple
> clocks (example on OMAP, where there are the concepts of interface,
> functional and optional clocks per block), appropriate handling will
> be necessary from pm_domain callbacks. So, the question about which
> clock rate is being controlled or returned to is entirely upto the
> pm_domain implementation.
>
> On the otherhand, we can debate about defining and querying ACPI style
> "Performance state" instead of frequencies and wrap P-states inside
> or the other way around.. but given that majority of drivers using
> pm_runtime would rather be interested in frequencies and my naieve
> belief that we can index P-states with frequencies, kind of influenced
> my choice here of proposing frequencies as base query parameter..
> ofcourse, debate is still open here.
>
> Yes, we can still debate if providing yet another wrapper on top of
> clock APIs makes sense at all as well.
>
> Nyet-signed-off-by: Nishanth Menon <nm@ti.com>
> ---
> drivers/base/power/runtime.c | 101 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/pm.h | 25 +++++++++--
> include/linux/pm_runtime.h | 21 +++++++++
> 3 files changed, 143 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
> index 72e00e6..ef230b4 100644
> --- a/drivers/base/power/runtime.c
> +++ b/drivers/base/power/runtime.c
> @@ -1401,3 +1401,104 @@ void pm_runtime_remove(struct device *dev)
> if (dev->power.irq_safe && dev->parent)
> pm_runtime_put(dev->parent);
> }
> +
> +/**
> + * pm_runtime_get_rate() - Returns the device operational frequency
> + * @dev: Device to handle
> + * @rate: Returns rate in Hz.
> + *
> + * Returns appropriate error value in case of error conditions, else
> + * returns 0 and rate is updated. The pm_domain logic does all the necessary
> + * operation (which may consider magic hardware stuff) to provide the rate.
> + *
> + * NOTE: the rate returned is a snapshot and in many cases just a bypass
> + * to clk api to set the rate.
> + */
> +int pm_runtime_get_rate(struct device *dev, unsigned long *rate)
> +{
> + unsigned long flags;
> + int error = -ENOSYS;
> +
> + if (!rate || !dev)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&dev->power.lock, flags);
> + if (!pm_runtime_active(dev)) {
> + error = -EINVAL;
> + goto out;
> + }
> +
> + if (dev->pm_domain && dev->pm_domain->active_ops.get_rate)
> + error = dev->pm_domain->active_ops.get_rate(dev, rate);
> +out:
> + spin_unlock_irqrestore(&dev->power.lock, flags);
> +
> + return error;
> +}
IMHO coupling device drivers even more with pm_runtime is wrong, and
Kevin Hilman seems to agree [1].
I would much rather go with Nishanth's initial approach of subscribing
to clock notifiers. They are, after all, supposed to tell the kernel
about any clock changes. In just so happens that in the case discussed
in this thread, OMAP needs to change voltages to match clock frequency
and, IMHO, using clock notifiers for that is correct.
The sematics are well defined and it's something which has been in the
tree for quite some time.
[1] https://lkml.org/lkml/2014/1/30/469
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v12 1/4] PHY: Add function set_speed to generic PHY framework
From: Felipe Balbi @ 2014-02-27 19:01 UTC (permalink / raw)
To: Loc Ho
Cc: devicetree, arnd, linux-scsi, linux-ide, jcm, patches,
linux-kernel, kishon, olof, ddutile, tj, linux-arm-kernel
In-Reply-To: <1393524848-8207-2-git-send-email-lho@apm.com>
[-- Attachment #1.1: Type: text/plain, Size: 426 bytes --]
Hi,
On Thu, Feb 27, 2014 at 11:14:05AM -0700, Loc Ho wrote:
> This patch adds function set_speed to the generic PHY framework operation
> structure. This function can be called to instruct the PHY underlying layer
> at specified lane to configure for specified speed in hertz.
why ? looks like clk_set_rate() is your friend here. Can you be more
descriptive of the use case ? When will this be used ?
--
balbi
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v12 3/4] PHY: add APM X-Gene SoC 15Gbps Multi-purpose PHY driver
From: Felipe Balbi @ 2014-02-27 19:02 UTC (permalink / raw)
To: Loc Ho
Cc: devicetree, Suman Tripathi, arnd, linux-scsi, linux-ide, jcm,
patches, linux-kernel, kishon, olof, ddutile, tj, Tuan Phan,
linux-arm-kernel
In-Reply-To: <1393524848-8207-4-git-send-email-lho@apm.com>
[-- Attachment #1.1: Type: text/plain, Size: 482 bytes --]
Hi,
On Thu, Feb 27, 2014 at 11:14:07AM -0700, Loc Ho wrote:
> +/*
> + * This function is used to configure the PHY to operation as either SATA Gen1
> + * or Gen2 speed.
> + */
> +static void xgene_phy_sata_force_gen(struct xgene_phy_ctx *ctx,
> + int lane, int gen)
why do you need to *force* the generation ? Is this because of some
silicon errata ? It almost seems like this should be done through link
negotiation between both link partners.
--
balbi
[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 1/4] pci: OF: Fix the conversion of IO ranges into IO resources.
From: Liviu Dudau @ 2014-02-27 19:12 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: devicetree@vger.kernel.org, linaro-kernel, linux-pci, Liviu Dudau,
LKML, Will Deacon, Catalin Marinas, Bjorn Helgaas, LAKML
In-Reply-To: <20140227181951.GC24656@obsidianresearch.com>
On Thu, Feb 27, 2014 at 11:19:51AM -0700, Jason Gunthorpe wrote:
> On Thu, Feb 27, 2014 at 01:06:39PM +0000, Liviu Dudau wrote:
> > + if (res->flags & IORESOURCE_IO) {
> > + unsigned long port;
> > + port = pci_address_to_pio(range->pci_addr);
>
> This looks very suspicious, pci_addr is not unique across all domains,
> so there is no way to convert from a pci_addr to the virtual IO
> address without knowing the domain number as well.
>
> I would like to see it be:
> port = pci_address_to_pio(range->cpu_addr);
>
> cpu_addr is unique across all domains.
Jason,
First thanks for reviewing the updated series.
We have agreed early on that indeed using range->cpu_addr is the correct aproach
and me taking the lazy shortcut of using range->pci_addr because of the broken
default implementation of pci_address_to_pio is wrong. I will fix this for v3.
The outstanding issue is how to fix pci_address_to_pio() as it will not
for for range->cpu_addr > IO_SPACE_LIMIT (16MB in my case).
Best regards,
Liviu
>
> Looking at the microblaze and PPC versions I think the above version
> is actually correct (assuming io_base_phys is the CPU address of the
> IO window)
>
> Jason
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH v8.1 1/6] dt-bindings: sram: describe option to reserve parts of the memory
From: Arnd Bergmann @ 2014-02-27 19:16 UTC (permalink / raw)
To: Heiko Stübner
Cc: Mark Rutland, devicetree, Pawel Moll, Stephen Warren,
Sachin Kamat, Greg Kroah-Hartman, Ian Campbell, linux-kernel, arm,
Philipp Zabel, Grant Likely, linux-arm-kernel@lists.infradead.org
In-Reply-To: <2574291.evU61mFTUd@phil>
On Wednesday 26 February 2014, Heiko Stübner wrote:
> Some SoCs need parts of their sram for special purposes. So while being part
> of the peripheral, it should not be part of the genpool controlling the sram.
>
> Therefore add the option to define reserved regions as subnodes of the
> sram-node similar to defining reserved global memory regions.
>
> Originally
> Suggested-by: Rob Herring <robherring2@gmail.com>
>
> Using subnodes for reserved regions
> Suggested-by: Grant Likely <grant.likely@linaro.org>
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> Tested-by: Ulrich Prinz <ulrich.prinz@googlemail.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* [PATCH v4 0/3] ARM: sun7i/sun6i: irqchip: Irqchip driver for NMI
From: Carlo Caione @ 2014-02-27 19:34 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, mark.rutland-5wv7dgnIgG8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Carlo Caione
Allwinner A20/A31 SoCs have a special interrupt controller for managing NMI.
Three register are present to (un)mask, control and acknowledge NMI.
These two patches add a new irqchip driver in cascade with GIC.
Changes since v1:
- added binding document
Changes since v2:
- fixed trigger type in DTS
- new explanations in binding documentation
- added support for A31 (sun6i)
Changes since v3:
- changed compatibles
Carlo Caione (3):
ARM: sun7i/sun6i: irqchip: Add irqchip driver for NMI controller
ARM: sun7i/sun6i: dts: Add NMI irqchip support
ARM: sun7i/sun6i: irqchip: Update the documentation
.../allwinner,sun67i-sc-nmi.txt | 27 +++
arch/arm/boot/dts/sun6i-a31.dtsi | 9 +
arch/arm/boot/dts/sun7i-a20.dtsi | 9 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-sunxi-nmi.c | 228 +++++++++++++++++++++
5 files changed, 274 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt
create mode 100644 drivers/irqchip/irq-sunxi-nmi.c
--
1.8.3.2
^ permalink raw reply
* [PATCH v4 1/3] ARM: sun7i/sun6i: irqchip: Add irqchip driver for NMI controller
From: Carlo Caione @ 2014-02-27 19:34 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, mark.rutland-5wv7dgnIgG8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Carlo Caione
In-Reply-To: <1393529662-8663-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Allwinner A20/A31 SoCs have special registers to control / (un)mask /
acknowledge NMI. This NMI controller is separated and independent from GIC.
This patch adds a new irqchip to manage NMI.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-sunxi-nmi.c | 228 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 229 insertions(+)
create mode 100644 drivers/irqchip/irq-sunxi-nmi.c
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c60b901..e31d4d6 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o
obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o
obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o
+obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o
obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
obj-$(CONFIG_ARM_GIC) += irq-gic.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c
new file mode 100644
index 0000000..8796ae0
--- /dev/null
+++ b/drivers/irqchip/irq-sunxi-nmi.c
@@ -0,0 +1,228 @@
+/*
+ * Allwinner A20/A31 SoCs NMI IRQ chip driver.
+ *
+ * Carlo Caione <carlo.caione-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip/chained_irq.h>
+#include "irqchip.h"
+
+#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003
+
+enum {
+ SUNXI_SRC_TYPE_LEVEL_LOW = 0,
+ SUNXI_SRC_TYPE_EDGE_FALLING,
+ SUNXI_SRC_TYPE_LEVEL_HIGH,
+ SUNXI_SRC_TYPE_EDGE_RISING,
+};
+
+struct sunxi_sc_nmi_reg_offs {
+ u32 ctrl;
+ u32 pend;
+ u32 enable;
+};
+
+static struct sunxi_sc_nmi_reg_offs sun7i_reg_offs = {
+ .ctrl = 0x00,
+ .pend = 0x04,
+ .enable = 0x08,
+};
+
+static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = {
+ .ctrl = 0x00,
+ .pend = 0x04,
+ .enable = 0x34,
+};
+
+/*
+ * Ack level interrupts right before unmask
+ *
+ * In case of level-triggered interrupt, IRQ line must be acked before it
+ * is unmasked or else a double-interrupt is triggered
+ */
+
+static void sunxi_sc_nmi_ack_and_unmask(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ u32 mask = d->mask;
+
+ if (irqd_get_trigger_type(d) & IRQ_TYPE_LEVEL_MASK)
+ ct->chip.irq_ack(d);
+
+ irq_gc_lock(gc);
+ irq_reg_writel(mask, gc->reg_base + ct->regs.mask);
+ irq_gc_unlock(gc);
+}
+
+static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
+ u32 val)
+{
+ irq_reg_writel(val, gc->reg_base + off);
+}
+
+static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
+{
+ return irq_reg_readl(gc->reg_base + off);
+}
+
+static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_get_chip(irq);
+ unsigned int virq = irq_find_mapping(domain, 0);
+
+ chained_irq_enter(chip, desc);
+ generic_handle_irq(virq);
+ chained_irq_exit(chip, desc);
+}
+
+static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ struct irq_chip_type *ct = gc->chip_types;
+ u32 src_type_reg;
+ u32 ctrl_off = ct->regs.type;
+ unsigned int src_type;
+ unsigned int i;
+
+ irq_gc_lock(gc);
+
+ switch (flow_type & IRQF_TRIGGER_MASK) {
+ case IRQ_TYPE_EDGE_FALLING:
+ src_type = SUNXI_SRC_TYPE_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ src_type = SUNXI_SRC_TYPE_EDGE_RISING;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ src_type = SUNXI_SRC_TYPE_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_NONE:
+ case IRQ_TYPE_LEVEL_LOW:
+ src_type = SUNXI_SRC_TYPE_LEVEL_LOW;
+ break;
+ default:
+ irq_gc_unlock(gc);
+ pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n",
+ __func__, data->irq);
+ return -EBADR;
+ }
+
+ irqd_set_trigger_type(data, flow_type);
+ irq_setup_alt_chip(data, flow_type);
+
+ for (i = 0; i <= gc->num_ct; i++, ct++)
+ if (ct->type & flow_type)
+ ctrl_off = ct->regs.type;
+
+ src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off);
+ src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK;
+ src_type_reg |= src_type;
+ sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg);
+
+ irq_gc_unlock(gc);
+
+ return IRQ_SET_MASK_OK;
+}
+
+static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
+ struct sunxi_sc_nmi_reg_offs *reg_offs)
+{
+ struct irq_domain *domain;
+ struct irq_chip_generic *gc;
+ unsigned int irq;
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int ret;
+
+
+ domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
+ if (!domain) {
+ pr_err("%s: Could not register interrupt domain.\n", node->name);
+ return -ENOMEM;
+ }
+
+ ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name,
+ handle_level_irq, clr, 0,
+ IRQ_GC_INIT_MASK_CACHE);
+ if (ret) {
+ pr_err("%s: Could not allocate generic interrupt chip.\n",
+ node->name);
+ goto fail_irqd_remove;
+ }
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (irq <= 0) {
+ pr_err("%s: unable to parse irq\n", node->name);
+ ret = -EINVAL;
+ goto fail_irqd_remove;
+ }
+
+ gc = irq_get_domain_generic_chip(domain, 0);
+ gc->reg_base = of_iomap(node, 0);
+ if (!gc->reg_base) {
+ pr_err("%s: unable to map resource\n", node->name);
+ ret = -ENOMEM;
+ goto fail_irqd_remove;
+ }
+
+ gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
+ gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[0].chip.irq_unmask = sunxi_sc_nmi_ack_and_unmask;
+ gc->chip_types[0].chip.irq_set_type = sunxi_sc_nmi_set_type;
+ gc->chip_types[0].regs.ack = reg_offs->pend;
+ gc->chip_types[0].regs.mask = reg_offs->enable;
+ gc->chip_types[0].regs.type = reg_offs->ctrl;
+
+ gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types[1].chip.name = gc->chip_types[0].chip.name;
+ gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types[1].chip.irq_unmask = sunxi_sc_nmi_ack_and_unmask;
+ gc->chip_types[1].chip.irq_set_type = sunxi_sc_nmi_set_type;
+ gc->chip_types[1].regs.ack = reg_offs->pend;
+ gc->chip_types[1].regs.mask = reg_offs->enable;
+ gc->chip_types[1].regs.type = reg_offs->ctrl;
+ gc->chip_types[1].handler = handle_edge_irq;
+
+ irq_set_handler_data(irq, domain);
+ irq_set_chained_handler(irq, sunxi_sc_nmi_handle_irq);
+
+ sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
+ sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1);
+
+ return 0;
+
+fail_irqd_remove:
+ irq_domain_remove(domain);
+
+ return ret;
+}
+
+static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs);
+}
+IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init);
+
+static int __init sun7i_sc_nmi_irq_init(struct device_node *node,
+ struct device_node *parent)
+{
+ return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs);
+}
+IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init);
--
1.8.3.2
^ permalink raw reply related
* [PATCH v4 2/3] ARM: sun7i/sun6i: dts: Add NMI irqchip support
From: Carlo Caione @ 2014-02-27 19:34 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, mark.rutland-5wv7dgnIgG8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Carlo Caione
In-Reply-To: <1393529662-8663-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
This patch adds DTS entries for NMI controller as child of GIC.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
arch/arm/boot/dts/sun6i-a31.dtsi | 9 +++++++++
arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 5256ad9..4a5050c 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -190,6 +190,15 @@
#size-cells = <1>;
ranges;
+ nmi_intc: sc-nmi-intc@01f00c0c {
+ compatible = "allwinner,sun6i-a31-sc-nmi";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x01f00c0c 0x38>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 0 4>;
+ };
+
pio: pinctrl@01c20800 {
compatible = "allwinner,sun6i-a31-pinctrl";
reg = <0x01c20800 0x400>;
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 4c25f81..f01a23c 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -310,6 +310,15 @@
#size-cells = <1>;
ranges;
+ nmi_intc: sc-nmi-intc@01c00030 {
+ compatible = "allwinner,sun7i-a20-sc-nmi";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x01c00030 0x0c>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 0 4>;
+ };
+
emac: ethernet@01c0b000 {
compatible = "allwinner,sun4i-emac";
reg = <0x01c0b000 0x1000>;
--
1.8.3.2
^ permalink raw reply related
* [PATCH v4 3/3] ARM: sun7i/sun6i: irqchip: Update the documentation
From: Carlo Caione @ 2014-02-27 19:34 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, mark.rutland-5wv7dgnIgG8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Carlo Caione
In-Reply-To: <1393529662-8663-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Added documentation for NMI irqchip.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
.../allwinner,sun67i-sc-nmi.txt | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt
new file mode 100644
index 0000000..ac31487
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt
@@ -0,0 +1,27 @@
+Allwinner Sunxi NMI Controller
+==============================
+
+Required properties:
+
+- compatible : should be "allwinner,sun7i-a20-sc-nmi" or
+ "allwinner,sun6i-a31-sc-nmi"
+- reg : Specifies base physical address and size of the registers.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+ interrupt source. The value shall be 2. The first cell is the IRQ number, the
+ second cell the trigger type.
+- interrupt-parent: Specifies the parent interrupt controller.
+- interrupts: Specifies the list of interrupt lines which are handled by
+ the interrupt controller in the parent controller's notation. This value
+ shall be the NMI.
+
+Example:
+
+sc-nmi-intc@01c00030 {
+ compatible = "allwinner,sun7i-a20-sc-nmi";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x01c00030 0x0c>;
+ interrupt-parent = <&gic>;
+ interrupts = <0 0 4>;
+};
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH v2 1/4] pci: OF: Fix the conversion of IO ranges into IO resources.
From: Jason Gunthorpe @ 2014-02-27 19:36 UTC (permalink / raw)
To: Liviu Dudau, linux-pci, Bjorn Helgaas, Catalin Marinas,
Will Deacon, linaro-kernel,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, LKML, LAKML
In-Reply-To: <20140227191259.GA31753-hOhETlTuV5niMG9XS5x8Mg@public.gmane.org>
On Thu, Feb 27, 2014 at 07:12:59PM +0000, Liviu Dudau wrote:
> The outstanding issue is how to fix pci_address_to_pio() as it will not
> for for range->cpu_addr > IO_SPACE_LIMIT (16MB in my case).
The default actually looks fine to me, it is the correct behavior for
systems that actually have a dedicated IO space (like x86) where the
'CPU' value for IO is the exact value used in the IO accessor
instructions. In this case the IO_SPACE_LIMIT test is appropriate.
It also looks correct for architectures that use the CPU MMIO address
as the IO address directly (where IO_SPACE_LIMIT would be 4G)
Architectures that use the virtual IO window technique will always
require a custom pci_address_to_pio implementation.
BTW, something that occured to me after reading the patches:
For ARM64 you might want to think about doing away with the fixed
virtual IO window like we see in ARM32. Just use the CPU MMIO address
directly within the kernel, and implement a ioport_map to setup the MM
on demand.
I think the legacy reasons for having all those layers of translation
are probably not applicable to ARM64, and it is much simpler without
the extra translation step....
Arnd, what do you think?
Jason
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 1/4] pci: OF: Fix the conversion of IO ranges into IO resources.
From: Arnd Bergmann @ 2014-02-27 19:48 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Jason Gunthorpe, Liviu Dudau, linux-pci, Bjorn Helgaas,
Catalin Marinas, Will Deacon, linaro-kernel,
devicetree@vger.kernel.org, LKML
In-Reply-To: <20140227193627.GA7773@obsidianresearch.com>
On Thursday 27 February 2014 12:36:27 Jason Gunthorpe wrote:
> On Thu, Feb 27, 2014 at 07:12:59PM +0000, Liviu Dudau wrote:
> > The outstanding issue is how to fix pci_address_to_pio() as it will not
> > for for range->cpu_addr > IO_SPACE_LIMIT (16MB in my case).
>
> The default actually looks fine to me, it is the correct behavior for
> systems that actually have a dedicated IO space (like x86) where the
> 'CPU' value for IO is the exact value used in the IO accessor
> instructions. In this case the IO_SPACE_LIMIT test is appropriate.
Right.
> It also looks correct for architectures that use the CPU MMIO address
> as the IO address directly (where IO_SPACE_LIMIT would be 4G)
Are you aware of any that still do? I thought we had stopped doing
that.
> Architectures that use the virtual IO window technique will always
> require a custom pci_address_to_pio implementation.
Hmm, at the moment we only call it from of_address_to_resource(),
which in turn does not get called on PCI devices, and does not
call pci_address_to_pio for 'simple' platform devices. The only
case I can think of where it actually matters is when we have
ISA devices in DT that use an I/O port address in the reg property,
and that case hopefully won't happen on ARM32 or ARM64.
> BTW, something that occured to me after reading the patches:
>
> For ARM64 you might want to think about doing away with the fixed
> virtual IO window like we see in ARM32. Just use the CPU MMIO address
> directly within the kernel, and implement a ioport_map to setup the MM
> on demand.
>
> I think the legacy reasons for having all those layers of translation
> are probably not applicable to ARM64, and it is much simpler without
> the extra translation step....
>
> Arnd, what do you think?
Either I don't like it or I misunderstand you ;-)
Most PCI drivers normally don't call ioport_map or pci_iomap, so
we can't just do it there. If you are thinking of calling ioport_map
for every PCI device that has an I/O BAR and storing the virtual
address in the pci_dev resource, I don't see what that gains us
in terms of complexity, and it will also break /dev/port.
Arnd
^ 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