From 5677eab3c86d47a40105295719e24d419f8314c1 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 May 2019 09:56:24 +0100 Subject: [PATCH 001/123] soc: fsl: fix spelling mistake "Firmaware" -> "Firmware" There is a spelling mistake in a pr_err message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Li Yang --- drivers/soc/fsl/dpaa2-console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/fsl/dpaa2-console.c b/drivers/soc/fsl/dpaa2-console.c index 9168d8ddc932..27243f706f37 100644 --- a/drivers/soc/fsl/dpaa2-console.c +++ b/drivers/soc/fsl/dpaa2-console.c @@ -73,7 +73,7 @@ static u64 get_mc_fw_base_address(void) mcfbaregs = ioremap(mc_base_addr.start, resource_size(&mc_base_addr)); if (!mcfbaregs) { - pr_err("could not map MC Firmaware Base registers\n"); + pr_err("could not map MC Firmware Base registers\n"); return 0; } From f03de74d92aca6a5399c088074550a164ae44eec Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:14:54 +0000 Subject: [PATCH 002/123] soc: fsl: qe: drop useless static qualifier The local variable snum_init has no reason to have static storage duration. Reviewed-by: Christophe Leroy Reviewed-by: Qiang Zhao Signed-off-by: Rasmus Villemoes Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 612d9c551be5..855373deb746 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -306,7 +306,7 @@ static void qe_snums_init(void) 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, }; - static const u8 *snum_init; + const u8 *snum_init; qe_num_of_snum = qe_get_num_of_snums(); From 875f2aabfcfa321dd39298849e3833b44ee54b4d Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:14:56 +0000 Subject: [PATCH 003/123] soc: fsl: qe: reduce static memory footprint by 1.7K The current array of struct qe_snum use 256*4 bytes for just keeping track of the free/used state of each index, and the struct layout means there's another 768 bytes of padding. If we just unzip that structure, the array of snum values just use 256 bytes, while the free/inuse state can be tracked in a 32 byte bitmap. So this reduces the .data footprint by 1760 bytes. It also serves as preparation for introducing another DT binding for specifying the snum values. Reviewed-by: Christophe Leroy Reviewed-by: Qiang Zhao Signed-off-by: Rasmus Villemoes Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 42 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 855373deb746..4b59109df22b 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -14,6 +14,7 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ +#include #include #include #include @@ -43,25 +44,14 @@ static DEFINE_SPINLOCK(qe_lock); DEFINE_SPINLOCK(cmxgcr_lock); EXPORT_SYMBOL(cmxgcr_lock); -/* QE snum state */ -enum qe_snum_state { - QE_SNUM_STATE_USED, - QE_SNUM_STATE_FREE -}; - -/* QE snum */ -struct qe_snum { - u8 num; - enum qe_snum_state state; -}; - /* We allocate this here because it is used almost exclusively for * the communication processor devices. */ struct qe_immap __iomem *qe_immr; EXPORT_SYMBOL(qe_immr); -static struct qe_snum snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ +static u8 snums[QE_NUM_OF_SNUM]; /* Dynamically allocated SNUMs */ +static DECLARE_BITMAP(snum_state, QE_NUM_OF_SNUM); static unsigned int qe_num_of_snum; static phys_addr_t qebase = -1; @@ -315,10 +305,8 @@ static void qe_snums_init(void) else snum_init = snum_init_46; - for (i = 0; i < qe_num_of_snum; i++) { - snums[i].num = snum_init[i]; - snums[i].state = QE_SNUM_STATE_FREE; - } + bitmap_zero(snum_state, QE_NUM_OF_SNUM); + memcpy(snums, snum_init, qe_num_of_snum); } int qe_get_snum(void) @@ -328,12 +316,10 @@ int qe_get_snum(void) int i; spin_lock_irqsave(&qe_lock, flags); - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].state == QE_SNUM_STATE_FREE) { - snums[i].state = QE_SNUM_STATE_USED; - snum = snums[i].num; - break; - } + i = find_first_zero_bit(snum_state, qe_num_of_snum); + if (i < qe_num_of_snum) { + set_bit(i, snum_state); + snum = snums[i]; } spin_unlock_irqrestore(&qe_lock, flags); @@ -343,14 +329,10 @@ EXPORT_SYMBOL(qe_get_snum); void qe_put_snum(u8 snum) { - int i; + const u8 *p = memchr(snums, snum, qe_num_of_snum); - for (i = 0; i < qe_num_of_snum; i++) { - if (snums[i].num == snum) { - snums[i].state = QE_SNUM_STATE_FREE; - break; - } - } + if (p) + clear_bit(p - snums, snum_state); } EXPORT_SYMBOL(qe_put_snum); From d7fc5963664198d25ca525f120f72ff053ba334f Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:14:57 +0000 Subject: [PATCH 004/123] soc: fsl: qe: introduce qe_get_device_node helper The 'try of_find_compatible_node(NULL, NULL, "fsl,qe"), fall back to of_find_node_by_type(NULL, "qe")' pattern is repeated five times. Factor it into a common helper. Reviewed-by: Christophe Leroy Reviewed-by: Qiang Zhao Signed-off-by: Rasmus Villemoes Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 71 +++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 4b59109df22b..4b444846d590 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -56,6 +56,20 @@ static unsigned int qe_num_of_snum; static phys_addr_t qebase = -1; +static struct device_node *qe_get_device_node(void) +{ + struct device_node *qe; + + /* + * Newer device trees have an "fsl,qe" compatible property for the QE + * node, but we still need to support older device trees. + */ + qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (qe) + return qe; + return of_find_node_by_type(NULL, "qe"); +} + static phys_addr_t get_qe_base(void) { struct device_node *qe; @@ -65,12 +79,9 @@ static phys_addr_t get_qe_base(void) if (qebase != -1) return qebase; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return qebase; - } + qe = qe_get_device_node(); + if (!qe) + return qebase; ret = of_address_to_resource(qe, 0, &res); if (!ret) @@ -164,12 +175,9 @@ unsigned int qe_get_brg_clk(void) if (brg_clk) return brg_clk; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return brg_clk; - } + qe = qe_get_device_node(); + if (!qe) + return brg_clk; prop = of_get_property(qe, "brg-frequency", &size); if (prop && size == sizeof(*prop)) @@ -558,16 +566,9 @@ struct qe_firmware_info *qe_get_firmware_info(void) initialized = 1; - /* - * Newer device trees have an "fsl,qe" compatible property for the QE - * node, but we still need to support older device trees. - */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return NULL; - } + qe = qe_get_device_node(); + if (!qe) + return NULL; /* Find the 'firmware' child node */ fw = of_get_child_by_name(qe, "firmware"); @@ -613,16 +614,9 @@ unsigned int qe_get_num_of_risc(void) unsigned int num_of_risc = 0; const u32 *prop; - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_risc; - } + qe = qe_get_device_node(); + if (!qe) + return num_of_risc; prop = of_get_property(qe, "fsl,qe-num-riscs", &size); if (prop && size == sizeof(*prop)) @@ -642,16 +636,9 @@ unsigned int qe_get_num_of_snums(void) const u32 *prop; num_of_snums = 28; /* The default number of snum for threads is 28 */ - qe = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!qe) { - /* Older devices trees did not have an "fsl,qe" - * compatible property, so we need to look for - * the QE node by name. - */ - qe = of_find_node_by_type(NULL, "qe"); - if (!qe) - return num_of_snums; - } + qe = qe_get_device_node(); + if (!qe) + return num_of_snums; prop = of_get_property(qe, "fsl,qe-num-snums", &size); if (prop && size == sizeof(*prop)) { From 8f762fe5819f8561343cf02e234789744ed68a98 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:14:58 +0000 Subject: [PATCH 005/123] dt-bindings: soc: fsl: qe: document new fsl,qe-snums binding Reading table 4-30, and its footnotes, of the QUICC Engine Block Reference Manual shows that the set of snum _values_ is not necessarily just a function of the _number_ of snums, as given in the fsl,qe-num-snums property. As an alternative, to make it easier to add support for other variants of the QUICC engine IP, this introduces a new binding fsl,qe-snums, which automatically encodes both the number of snums and the actual values to use. Signed-off-by: Rasmus Villemoes Reviewed-by: Rob Herring Signed-off-by: Li Yang --- .../devicetree/bindings/soc/fsl/cpm_qe/qe.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe.txt b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe.txt index d7afaff5faff..05ec2a838c54 100644 --- a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe.txt +++ b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe.txt @@ -18,7 +18,8 @@ Required properties: - reg : offset and length of the device registers. - bus-frequency : the clock frequency for QUICC Engine. - fsl,qe-num-riscs: define how many RISC engines the QE has. -- fsl,qe-num-snums: define how many serial number(SNUM) the QE can use for the +- fsl,qe-snums: This property has to be specified as '/bits/ 8' value, + defining the array of serial number (SNUM) values for the virtual threads. Optional properties: @@ -34,6 +35,11 @@ Recommended properties - brg-frequency : the internal clock source frequency for baud-rate generators in Hz. +Deprecated properties +- fsl,qe-num-snums: define how many serial number(SNUM) the QE can use + for the threads. Use fsl,qe-snums instead to not only specify the + number of snums, but also their values. + Example: qe@e0100000 { #address-cells = <1>; @@ -44,6 +50,11 @@ Example: reg = ; brg-frequency = <0>; bus-frequency = <179A7B00>; + fsl,qe-snums = /bits/ 8 < + 0x04 0x05 0x0C 0x0D 0x14 0x15 0x1C 0x1D + 0x24 0x25 0x2C 0x2D 0x34 0x35 0x88 0x89 + 0x98 0x99 0xA8 0xA9 0xB8 0xB9 0xC8 0xC9 + 0xD8 0xD9 0xE8 0xE9>; } * Multi-User RAM (MURAM) From 5cfca8911ddd59150ba53cff16956eabe1b3dcac Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:15:00 +0000 Subject: [PATCH 006/123] soc: fsl: qe: support fsl,qe-snums property Add driver support for the newly introduced fsl,qe-snums property. Conveniently, of_property_read_variable_u8_array does exactly what we need: If the property fsl,qe-snums is found (and has an allowed size), the array of values get copied to snums, and the return value is the number of snums - we cannot assign directly to num_of_snums, since we need to check whether the return value is negative. Reviewed-by: Christophe Leroy Reviewed-by: Qiang Zhao Signed-off-by: Rasmus Villemoes Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 4b444846d590..1d27187b251c 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -283,7 +283,6 @@ EXPORT_SYMBOL(qe_clock_source); */ static void qe_snums_init(void) { - int i; static const u8 snum_init_76[] = { 0x04, 0x05, 0x0C, 0x0D, 0x14, 0x15, 0x1C, 0x1D, 0x24, 0x25, 0x2C, 0x2D, 0x34, 0x35, 0x88, 0x89, @@ -304,7 +303,21 @@ static void qe_snums_init(void) 0x28, 0x29, 0x38, 0x39, 0x48, 0x49, 0x58, 0x59, 0x68, 0x69, 0x78, 0x79, 0x80, 0x81, }; + struct device_node *qe; const u8 *snum_init; + int i; + + bitmap_zero(snum_state, QE_NUM_OF_SNUM); + qe = qe_get_device_node(); + if (qe) { + i = of_property_read_variable_u8_array(qe, "fsl,qe-snums", + snums, 1, QE_NUM_OF_SNUM); + of_node_put(qe); + if (i > 0) { + qe_num_of_snum = i; + return; + } + } qe_num_of_snum = qe_get_num_of_snums(); @@ -313,7 +326,6 @@ static void qe_snums_init(void) else snum_init = snum_init_46; - bitmap_zero(snum_state, QE_NUM_OF_SNUM); memcpy(snums, snum_init, qe_num_of_snum); } From 21560067fb1f5e87abedc3ecec5d46f67ac0c019 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 May 2019 11:15:01 +0000 Subject: [PATCH 007/123] soc: fsl: qe: fold qe_get_num_of_snums into qe_snums_init The comment "No QE ever has fewer than 28 SNUMs" is false; e.g. the MPC8309 has 14. The code path returning -EINVAL is also a recipe for instant disaster, since the caller (qe_snums_init) uncritically assigns the return value to the unsigned qe_num_of_snum, and would thus proceed to attempt to copy 4GB from snum_init_46[] to the snum[] array. So fold the handling of the legacy fsl,qe-num-snums into qe_snums_init, and make sure we do not end up using the snum_init_46 array in cases other than the two where we know it makes sense. Reviewed-by: Christophe Leroy Reviewed-by: Qiang Zhao Signed-off-by: Rasmus Villemoes Signed-off-by: Li Yang --- drivers/soc/fsl/qe/qe.c | 46 ++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/drivers/soc/fsl/qe/qe.c b/drivers/soc/fsl/qe/qe.c index 1d27187b251c..852060caff24 100644 --- a/drivers/soc/fsl/qe/qe.c +++ b/drivers/soc/fsl/qe/qe.c @@ -308,24 +308,33 @@ static void qe_snums_init(void) int i; bitmap_zero(snum_state, QE_NUM_OF_SNUM); + qe_num_of_snum = 28; /* The default number of snum for threads is 28 */ qe = qe_get_device_node(); if (qe) { i = of_property_read_variable_u8_array(qe, "fsl,qe-snums", snums, 1, QE_NUM_OF_SNUM); - of_node_put(qe); if (i > 0) { + of_node_put(qe); qe_num_of_snum = i; return; } + /* + * Fall back to legacy binding of using the value of + * fsl,qe-num-snums to choose one of the static arrays + * above. + */ + of_property_read_u32(qe, "fsl,qe-num-snums", &qe_num_of_snum); + of_node_put(qe); } - qe_num_of_snum = qe_get_num_of_snums(); - - if (qe_num_of_snum == 76) + if (qe_num_of_snum == 76) { snum_init = snum_init_76; - else + } else if (qe_num_of_snum == 28 || qe_num_of_snum == 46) { snum_init = snum_init_46; - + } else { + pr_err("QE: unsupported value of fsl,qe-num-snums: %u\n", qe_num_of_snum); + return; + } memcpy(snums, snum_init, qe_num_of_snum); } @@ -642,30 +651,7 @@ EXPORT_SYMBOL(qe_get_num_of_risc); unsigned int qe_get_num_of_snums(void) { - struct device_node *qe; - int size; - unsigned int num_of_snums; - const u32 *prop; - - num_of_snums = 28; /* The default number of snum for threads is 28 */ - qe = qe_get_device_node(); - if (!qe) - return num_of_snums; - - prop = of_get_property(qe, "fsl,qe-num-snums", &size); - if (prop && size == sizeof(*prop)) { - num_of_snums = *prop; - if ((num_of_snums < 28) || (num_of_snums > QE_NUM_OF_SNUM)) { - /* No QE ever has fewer than 28 SNUMs */ - pr_err("QE: number of snum is invalid\n"); - of_node_put(qe); - return -EINVAL; - } - } - - of_node_put(qe); - - return num_of_snums; + return qe_num_of_snum; } EXPORT_SYMBOL(qe_get_num_of_snums); From 9f02b8f61f29f4518581770d57bfffe99b1ea599 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Wed, 3 Jul 2019 12:03:50 +0200 Subject: [PATCH 008/123] tee: optee: add might_sleep for RPC requests If the kernel is compiled with CONFIG_PREEMPT_VOLUNTARY and OP-TEE is executing a long running workload, the following errors are raised: [ 1705.971228] rcu: INFO: rcu_sched detected stalls on CPUs/tasks: [ 1705.977195] rcu: (detected by 0, t=2102 jiffies, g=51977, q=3) [ 1705.983152] rcu: All QSes seen, last rcu_sched kthread activity 2102 (140596-138494), jiffies_till_next_fqs=1, root ->qsmask 0x0 [ 1705.994729] optee-xtest R running task 0 169 157 0x00000002 While OP-TEE is returning regularly to the kernel due to timer interrupts, the OPTEE_SMC_FUNC_FOREIGN_INTR case does not contain an explicit rescheduling point. Add a might_sleep() to the RPC request case to ensure that the kernel can reschedule another task if OP-TEE requests RPC handling. Signed-off-by: Rouven Czerwinski Signed-off-by: Jens Wiklander --- drivers/tee/optee/call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index aa942703ae65..13b0269a0abc 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -148,6 +148,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) */ optee_cq_wait_for_completion(&optee->call_queue, &w); } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) { + might_sleep(); param.a0 = res.a0; param.a1 = res.a1; param.a2 = res.a2; From 3026d0b7a4972fd5fb56f5a454d1efe7534e84fc Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Thu, 18 Jul 2019 15:13:11 +0800 Subject: [PATCH 009/123] soc: imx8: Add i.MX8MQ UID(unique identifier) support Add i.MX8MQ SoC UID(unique identifier) support, user can read it from sysfs: root@imx8mqevk:~# cat /sys/devices/soc0/soc_uid D56911D6F060954B Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx8.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index f924ae8c6514..c19ef4b95668 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -16,6 +16,9 @@ #define IMX8MQ_SW_INFO_B1 0x40 #define IMX8MQ_SW_MAGIC_B1 0xff0055aa +#define OCOTP_UID_LOW 0x410 +#define OCOTP_UID_HIGH 0x420 + /* Same as ANADIG_DIGPROG_IMX7D */ #define ANADIG_DIGPROG_IMX8MM 0x800 @@ -24,6 +27,16 @@ struct imx8_soc_data { u32 (*soc_revision)(void); }; +static u64 soc_uid; + +static ssize_t soc_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%016llX\n", soc_uid); +} + +static DEVICE_ATTR_RO(soc_uid); + static u32 __init imx8mq_soc_revision(void) { struct device_node *np; @@ -42,6 +55,10 @@ static u32 __init imx8mq_soc_revision(void) if (magic == IMX8MQ_SW_MAGIC_B1) rev = REV_B1; + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); + soc_uid <<= 32; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + iounmap(ocotp_base); out: @@ -140,6 +157,11 @@ static int __init imx8_soc_init(void) goto free_rev; } + ret = device_create_file(soc_device_to_device(soc_dev), + &dev_attr_soc_uid); + if (ret) + goto free_rev; + if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); From 8dfe397431e0e05d9e66a9f20511833bda91f978 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 26 Jun 2019 15:44:15 +0800 Subject: [PATCH 010/123] soc: imx8: Add i.MX8MM UID(unique identifier) support Add i.MX8MM SoC UID(unique identifier) support, user can read it from sysfs: root@imx8mmevk:~# cat /sys/devices/soc0/soc_uid B365FA0A5C85D6EE Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx8.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index c19ef4b95668..b9831576dd25 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -66,6 +66,26 @@ out: return rev; } +static void __init imx8mm_soc_uid(void) +{ + void __iomem *ocotp_base; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp"); + if (!np) + return; + + ocotp_base = of_iomap(np, 0); + WARN_ON(!ocotp_base); + + soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); + soc_uid <<= 32; + soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW); + + iounmap(ocotp_base); + of_node_put(np); +} + static u32 __init imx8mm_soc_revision(void) { struct device_node *np; @@ -83,6 +103,9 @@ static u32 __init imx8mm_soc_revision(void) iounmap(anatop_base); of_node_put(np); + + imx8mm_soc_uid(); + return rev; } From 6e37ccf78a53296c6c7bf426065762c27829eb84 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 17 May 2019 14:09:21 -0700 Subject: [PATCH 011/123] firmware: qcom_scm: Use proper types for dma mappings We need to use the proper types and convert between physical addresses and dma addresses here to avoid mismatch warnings. This is especially important on systems with a different size for dma addresses and physical addresses. Otherwise, we get the following warning: drivers/firmware/qcom_scm.c: In function "qcom_scm_assign_mem": drivers/firmware/qcom_scm.c:469:47: error: passing argument 3 of "dma_alloc_coherent" from incompatible pointer type [-Werror=incompatible-pointer-types] We also fix the size argument to dma_free_coherent() because that size doesn't need to be aligned after it's already aligned on the allocation size. In fact, dma debugging expects the same arguments to be passed to both the allocation and freeing sides of the functions so changing the size is incorrect regardless. Reported-by: Ian Jackson Cc: Ian Jackson Cc: Julien Grall Cc: Bjorn Andersson Cc: Avaneesh Kumar Dwivedi Tested-by: Bjorn Andersson Signed-off-by: Stephen Boyd Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 2ddc118dba1b..74b84244a0db 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -440,6 +441,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, phys_addr_t mem_to_map_phys; phys_addr_t dest_phys; phys_addr_t ptr_phys; + dma_addr_t ptr_dma; size_t mem_to_map_sz; size_t dest_sz; size_t src_sz; @@ -457,9 +459,10 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(dest_sz, SZ_64); - ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_dma, GFP_KERNEL); if (!ptr) return -ENOMEM; + ptr_phys = dma_to_phys(__scm->dev, ptr_dma); /* Fill source vmid detail */ src = ptr; @@ -489,7 +492,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, ptr_phys, src_sz, dest_phys, dest_sz); - dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_dma); if (ret) { dev_err(__scm->dev, "Assign memory protection call failed %d.\n", ret); From c8b08fc0d6f83427973b94694b7ec8855d2d1e37 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 17 May 2019 14:09:23 -0700 Subject: [PATCH 012/123] firmware: qcom_scm: Fix some typos in docs and printks Some words are misspelled and we put a full stop after a return value integer. Fix these things up so it doesn't look so odd. Cc: Ian Jackson Cc: Julien Grall Cc: Bjorn Andersson Cc: Avaneesh Kumar Dwivedi Signed-off-by: Stephen Boyd Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 74b84244a0db..c7e63af91567 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -426,11 +426,11 @@ EXPORT_SYMBOL(qcom_scm_set_remote_state); * @mem_sz: size of the region. * @srcvm: vmid for current set of owners, each set bit in * flag indicate a unique owner - * @newvm: array having new owners and corrsponding permission + * @newvm: array having new owners and corresponding permission * flags * @dest_cnt: number of owners in next set. * - * Return negative errno on failure, 0 on success, with @srcvm updated. + * Return negative errno on failure or 0 on success with @srcvm updated. */ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *srcvm, @@ -495,7 +495,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_dma); if (ret) { dev_err(__scm->dev, - "Assign memory protection call failed %d.\n", ret); + "Assign memory protection call failed %d\n", ret); return -EINVAL; } From af311ff9a69189a03548efd5a47d4bb44644fd45 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 17 May 2019 14:09:22 -0700 Subject: [PATCH 013/123] firmware: qcom_scm: Cleanup code in qcom_scm_assign_mem() There are some questionable coding styles in this function. It looks quite odd to deref a pointer with array indexing that only uses the first element. Also, destroying an input/output variable halfway through the function and then overwriting it on success is not clear. It's better to use a local variable and the kernel macros to step through each bit set in a bitmask and clearly show where outputs are set. Cc: Ian Jackson Cc: Julien Grall Cc: Bjorn Andersson Cc: Avaneesh Kumar Dwivedi Tested-by: Bjorn Andersson Signed-off-by: Stephen Boyd [bjorn: Changed for_each_set_bit() size to BITS_PER_LONG] Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 34 ++++++++++++++++------------------ include/linux/qcom_scm.h | 9 +++++---- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index c7e63af91567..4802ab170fe5 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -434,7 +434,8 @@ EXPORT_SYMBOL(qcom_scm_set_remote_state); */ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *srcvm, - struct qcom_scm_vmperm *newvm, int dest_cnt) + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt) { struct qcom_scm_current_perm_info *destvm; struct qcom_scm_mem_map_info *mem_to_map; @@ -449,11 +450,10 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, int next_vm; __le32 *src; void *ptr; - int ret; - int len; - int i; + int ret, i, b; + unsigned long srcvm_bits = *srcvm; - src_sz = hweight_long(*srcvm) * sizeof(*src); + src_sz = hweight_long(srcvm_bits) * sizeof(*src); mem_to_map_sz = sizeof(*mem_to_map); dest_sz = dest_cnt * sizeof(*destvm); ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + @@ -466,28 +466,26 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, /* Fill source vmid detail */ src = ptr; - len = hweight_long(*srcvm); - for (i = 0; i < len; i++) { - src[i] = cpu_to_le32(ffs(*srcvm) - 1); - *srcvm ^= 1 << (ffs(*srcvm) - 1); - } + i = 0; + for_each_set_bit(b, &srcvm_bits, BITS_PER_LONG) + src[i++] = cpu_to_le32(b); /* Fill details of mem buff to map */ mem_to_map = ptr + ALIGN(src_sz, SZ_64); mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); - mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); - mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + mem_to_map->mem_addr = cpu_to_le64(mem_addr); + mem_to_map->mem_size = cpu_to_le64(mem_sz); next_vm = 0; /* Fill details of next vmid detail */ destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); - for (i = 0; i < dest_cnt; i++) { - destvm[i].vmid = cpu_to_le32(newvm[i].vmid); - destvm[i].perm = cpu_to_le32(newvm[i].perm); - destvm[i].ctx = 0; - destvm[i].ctx_size = 0; - next_vm |= BIT(newvm[i].vmid); + for (i = 0; i < dest_cnt; i++, destvm++, newvm++) { + destvm->vmid = cpu_to_le32(newvm->vmid); + destvm->perm = cpu_to_le32(newvm->perm); + destvm->ctx = 0; + destvm->ctx_size = 0; + next_vm |= BIT(newvm->vmid); } ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 3f12cc77fb58..2d5eff506e13 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -49,8 +49,9 @@ extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, extern int qcom_scm_pas_auth_and_reset(u32 peripheral); extern int qcom_scm_pas_shutdown(u32 peripheral); extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, - unsigned int *src, struct qcom_scm_vmperm *newvm, - int dest_cnt); + unsigned int *src, + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt); extern void qcom_scm_cpu_power_down(u32 flags); extern u32 qcom_scm_get_version(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); @@ -87,8 +88,8 @@ qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; } static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; } static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *src, - struct qcom_scm_vmperm *newvm, - int dest_cnt) { return -ENODEV; } + const struct qcom_scm_vmperm *newvm, + unsigned int dest_cnt) { return -ENODEV; } static inline void qcom_scm_cpu_power_down(u32 flags) {} static inline u32 qcom_scm_get_version(void) { return 0; } static inline u32 From 6d9d21711b38c9cd5f70e04623151f498f3129ac Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 18 Jul 2019 13:25:17 +0300 Subject: [PATCH 014/123] firmware: imx: scu-pd: Rename mu PD range to mu_a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Messaging Unit module enables two processors within the SoC to communicate and coordinate by passing messages through the MU interface. MUs have 2 “sides” with independent programming interfaces. Rename mu PD range to mu_a because it's actually side A of MUs. Signed-off-by: Daniel Baluta Reviewed-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 480cec69e2c9..950d30238186 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -92,7 +92,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, { "kpp", IMX_SC_R_KPP, 1, false, 0 }, { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, + { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ { "usb", IMX_SC_R_USB_0, 2, true, 0 }, From 590b346b58badfe87cb13a64abdc8af25e907846 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 18 Jul 2019 13:25:18 +0300 Subject: [PATCH 015/123] firmware: imx: scu-pd: Add mu13 b side PD range LSIO subsystem contains 14 MU instances. 5 MUs to communicate between AP <-> SCU - side-A PD range managed by AP - side-B PD range managed by SCU 9 MUs to communicate between all cores (AP/M4/DSP). - side-A PD range managed by core-A (AP/M4/DSP) - side-B PD range managed by core-B (AP/M4/DSP). Communication between AP <-> DSP is done through the assigned MU number 13. So, we power up side-A by the AP and we decide to power up side-B also from AP. This is because powering it up from DSP would be painful. Powering up side B from DSP would require the DSP to communicate with SCU and to keep things simple we don't want that now. Signed-off-by: Daniel Baluta Reviewed-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 950d30238186..eb9700b66a76 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -93,6 +93,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "kpp", IMX_SC_R_KPP, 1, false, 0 }, { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, + { "mu_b", IMX_SC_R_MU_13B, 1, true, 13 }, /* CONN SS */ { "usb", IMX_SC_R_USB_0, 2, true, 0 }, From d43dc52274d40a1543ca962008feb9ff784e3a49 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 18 Jul 2019 13:25:19 +0300 Subject: [PATCH 016/123] firmware: imx: scu-pd: Add IRQSTR_DSP PD range The DSP interrupt steer gathers interrupts from the system and can be used to steer them to DSP. Signed-off-by: Daniel Baluta Reviewed-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index eb9700b66a76..b556612207e5 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -131,6 +131,7 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, /* VPU SS */ { "vpu", IMX_SC_R_VPU, 1, false, 0 }, From 0ed0eb0171d7c19310d9f7b06a6dee82a4fb8ab6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 17 Jun 2019 14:00:56 +0200 Subject: [PATCH 017/123] soc: renesas: rcar-sysc: Use [] to denote a flexible array member Flexible array members should be denoted using [] instead of [0], else gcc will not warn when they are no longer at the end of the structure. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/soc/renesas/rcar-sysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 0c80fab4f8de..02b29ea62dc4 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -170,7 +170,7 @@ struct rcar_sysc_pd { struct generic_pm_domain genpd; struct rcar_sysc_ch ch; unsigned int flags; - char name[0]; + char name[]; }; static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) From e2557157a9dd47b0335cac325b4eb438db2320d2 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 5 Jul 2019 16:54:05 +0800 Subject: [PATCH 018/123] dt-bindings: reset: imx7: Add support for i.MX8MM i.MX8MM can reuse i.MX8MQ's reset driver, update the compatible property and related info to support i.MX8MM. Signed-off-by: Anson Huang Signed-off-by: Philipp Zabel --- .../bindings/reset/fsl,imx7-src.txt | 6 ++-- include/dt-bindings/reset/imx8mq-reset.h | 28 +++++++++---------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt index 13e095182db4..c2489e41a801 100644 --- a/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt +++ b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt @@ -8,6 +8,7 @@ Required properties: - compatible: - For i.MX7 SoCs should be "fsl,imx7d-src", "syscon" - For i.MX8MQ SoCs should be "fsl,imx8mq-src", "syscon" + - For i.MX8MM SoCs should be "fsl,imx8mm-src", "fsl,imx8mq-src", "syscon" - reg: should be register base and length as documented in the datasheet - interrupts: Should contain SRC interrupt @@ -46,5 +47,6 @@ Example: For list of all valid reset indices see - for i.MX7 and - for i.MX8MQ + for i.MX7, + for i.MX8MQ and + for i.MX8MM diff --git a/include/dt-bindings/reset/imx8mq-reset.h b/include/dt-bindings/reset/imx8mq-reset.h index 57c592498aa0..f17ef2a972f6 100644 --- a/include/dt-bindings/reset/imx8mq-reset.h +++ b/include/dt-bindings/reset/imx8mq-reset.h @@ -38,26 +38,26 @@ #define IMX8MQ_RESET_PCIEPHY_PERST 27 #define IMX8MQ_RESET_PCIE_CTRL_APPS_EN 28 #define IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF 29 -#define IMX8MQ_RESET_HDMI_PHY_APB_RESET 30 +#define IMX8MQ_RESET_HDMI_PHY_APB_RESET 30 /* i.MX8MM does NOT support */ #define IMX8MQ_RESET_DISP_RESET 31 #define IMX8MQ_RESET_GPU_RESET 32 #define IMX8MQ_RESET_VPU_RESET 33 -#define IMX8MQ_RESET_PCIEPHY2 34 -#define IMX8MQ_RESET_PCIEPHY2_PERST 35 -#define IMX8MQ_RESET_PCIE2_CTRL_APPS_EN 36 -#define IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF 37 -#define IMX8MQ_RESET_MIPI_CSI1_CORE_RESET 38 -#define IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET 39 -#define IMX8MQ_RESET_MIPI_CSI1_ESC_RESET 40 -#define IMX8MQ_RESET_MIPI_CSI2_CORE_RESET 41 -#define IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET 42 -#define IMX8MQ_RESET_MIPI_CSI2_ESC_RESET 43 +#define IMX8MQ_RESET_PCIEPHY2 34 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_PCIEPHY2_PERST 35 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_PCIE2_CTRL_APPS_EN 36 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF 37 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI1_CORE_RESET 38 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET 39 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI1_ESC_RESET 40 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI2_CORE_RESET 41 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET 42 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_MIPI_CSI2_ESC_RESET 43 /* i.MX8MM does NOT support */ #define IMX8MQ_RESET_DDRC1_PRST 44 #define IMX8MQ_RESET_DDRC1_CORE_RESET 45 #define IMX8MQ_RESET_DDRC1_PHY_RESET 46 -#define IMX8MQ_RESET_DDRC2_PRST 47 -#define IMX8MQ_RESET_DDRC2_CORE_RESET 48 -#define IMX8MQ_RESET_DDRC2_PHY_RESET 49 +#define IMX8MQ_RESET_DDRC2_PRST 47 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_DDRC2_CORE_RESET 48 /* i.MX8MM does NOT support */ +#define IMX8MQ_RESET_DDRC2_PHY_RESET 49 /* i.MX8MM does NOT support */ #define IMX8MQ_RESET_NUM 50 From 942b4c10b11bced65c0e553d8355b58bb3d9cdb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 1 Aug 2019 10:20:59 +0200 Subject: [PATCH 019/123] dt-bindings: reset: Fix typo in imx8mq resets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the mipi dsi resets were called IMX8MQ_RESET_MIPI_DIS__ instead of IMX8MQ_RESET_MIPI_DSI__ Since they're DSI related this looks like a typo. This fixes the only in tree user as well to not break bisecting. Signed-off-by: Guido Günther Reviewed-by: Lucas Stach Signed-off-by: Philipp Zabel --- drivers/reset/reset-imx7.c | 12 ++++++------ include/dt-bindings/reset/imx8mq-reset.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index 3ecd770f910b..1443a55a0c29 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c @@ -169,9 +169,9 @@ static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = { [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) }, [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) }, [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) }, - [IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, - [IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, - [IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, + [IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, + [IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, + [IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, BIT(2) | BIT(1) }, [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, @@ -220,9 +220,9 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev, case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */ - case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N: /* fallthrough */ + case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */ case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */ value = assert ? 0 : bit; diff --git a/include/dt-bindings/reset/imx8mq-reset.h b/include/dt-bindings/reset/imx8mq-reset.h index f17ef2a972f6..9a301082d361 100644 --- a/include/dt-bindings/reset/imx8mq-reset.h +++ b/include/dt-bindings/reset/imx8mq-reset.h @@ -31,9 +31,9 @@ #define IMX8MQ_RESET_OTG2_PHY_RESET 20 #define IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N 21 #define IMX8MQ_RESET_MIPI_DSI_RESET_N 22 -#define IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N 23 -#define IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N 24 -#define IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N 25 +#define IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N 23 +#define IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N 24 +#define IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N 25 #define IMX8MQ_RESET_PCIEPHY 26 #define IMX8MQ_RESET_PCIEPHY_PERST 27 #define IMX8MQ_RESET_PCIE_CTRL_APPS_EN 28 From 7c61ccf643a9aa308c96508dceb93afe2083afa5 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 1 Aug 2019 09:54:52 +0200 Subject: [PATCH 020/123] reset: reset-meson: update with SPDX Licence identifier Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel --- drivers/reset/reset-meson.c | 51 +------------------------------------ 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/drivers/reset/reset-meson.c b/drivers/reset/reset-meson.c index 5242e0679df7..7d05d766e1ea 100644 --- a/drivers/reset/reset-meson.c +++ b/drivers/reset/reset-meson.c @@ -1,58 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Amlogic Meson Reset Controller driver * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include From b16a0063654516bde54a95262d02207d79f1bf53 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 1 Aug 2019 09:54:53 +0200 Subject: [PATCH 021/123] dt-bindings: reset: amlogic,meson-gxbb-reset: update with SPDX Licence identifier Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel --- .../reset/amlogic,meson-gxbb-reset.h | 51 +------------------ 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h b/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h index 524d6077ac1b..ea5058618863 100644 --- a/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h +++ b/include/dt-bindings/reset/amlogic,meson-gxbb-reset.h @@ -1,56 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DT_BINDINGS_AMLOGIC_MESON_GXBB_RESET_H #define _DT_BINDINGS_AMLOGIC_MESON_GXBB_RESET_H From fb0d72c7ac5d1a3578e15cb56b4f453730790ba6 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 1 Aug 2019 09:54:54 +0200 Subject: [PATCH 022/123] dt-bindings: reset: amlogic,meson8b-reset: update with SPDX Licence identifier Signed-off-by: Neil Armstrong Signed-off-by: Philipp Zabel --- .../dt-bindings/reset/amlogic,meson8b-reset.h | 51 +------------------ 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/include/dt-bindings/reset/amlogic,meson8b-reset.h b/include/dt-bindings/reset/amlogic,meson8b-reset.h index 614aff2c7aff..c614438bcbdb 100644 --- a/include/dt-bindings/reset/amlogic,meson8b-reset.h +++ b/include/dt-bindings/reset/amlogic,meson8b-reset.h @@ -1,56 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * * Copyright (c) 2016 BayLibre, SAS. * Author: Neil Armstrong - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - * The full GNU General Public License is included in this distribution - * in the file called COPYING. - * - * BSD LICENSE - * - * Copyright (c) 2016 BayLibre, SAS. - * Author: Neil Armstrong - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DT_BINDINGS_AMLOGIC_MESON8B_RESET_H #define _DT_BINDINGS_AMLOGIC_MESON8B_RESET_H From 77266e722feabb6eefc8a7e84ac2415837d91c5f Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Fri, 12 Jul 2019 16:43:15 -0400 Subject: [PATCH 023/123] bus: imx-weim: optionally enable burst clock mode To enable burst clock mode, add the fsl,burst-clk-enable property to the weim bus's devicetree node. Example: weim: weim@21b8000 { compatible = "fsl,imx6q-weim"; reg = <0x021b8000 0x4000>; clocks = <&clks 196>; #address-cells = <2>; #size-cells = <1>; ranges = <0 0 0x08000000 0x08000000>; fsl,weim-cs-gpr = <&gpr>; fsl,burst-clk-enable; client-device@0,0 { compatible = "something"; reg = <0 0 0x02000000>; #address-cells = <1>; #size-cells = <1>; bank-width = <2>; fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 0x0000c000 0x1404a38e 0x00000000>; }; }; Signed-off-by: Sven Van Asbroeck Reviewed-by: Fabio Estevam Signed-off-by: Shawn Guo --- drivers/bus/imx-weim.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index db74334ca5ef..cb7d5504a22a 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -19,6 +19,8 @@ struct imx_weim_devtype { unsigned int cs_count; unsigned int cs_regs_count; unsigned int cs_stride; + unsigned int wcr_offset; + unsigned int wcr_bcm; }; static const struct imx_weim_devtype imx1_weim_devtype = { @@ -37,6 +39,8 @@ static const struct imx_weim_devtype imx50_weim_devtype = { .cs_count = 4, .cs_regs_count = 6, .cs_stride = 0x18, + .wcr_offset = 0x90, + .wcr_bcm = BIT(0), }; static const struct imx_weim_devtype imx51_weim_devtype = { @@ -192,6 +196,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, struct device_node *child; int ret, have_child = 0; struct cs_timing_state ts = {}; + u32 reg; if (devtype == &imx50_weim_devtype) { ret = imx_weim_gpr_setup(pdev); @@ -199,6 +204,17 @@ static int __init weim_parse_dt(struct platform_device *pdev, return ret; } + if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { + if (devtype->wcr_bcm) { + reg = readl(base + devtype->wcr_offset); + writel(reg | devtype->wcr_bcm, + base + devtype->wcr_offset); + } else { + dev_err(&pdev->dev, "burst clk mode not supported.\n"); + return -EINVAL; + } + } + for_each_available_child_of_node(pdev->dev.of_node, child) { ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); if (ret) From 3a760d986568b67d1f8411dab64608075817b90d Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 29 Jul 2019 15:02:17 +0200 Subject: [PATCH 024/123] soc: amlogic: meson-clk-measure: protect measure with a mutex In order to protect clock measuring when multiple process asks for a measure, protect the main measure function with mutexes. Reviewed-by: Kevin Hilman Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index 19d4cbc93a17..c470e24f1dfa 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -11,6 +11,8 @@ #include #include +static DEFINE_MUTEX(measure_lock); + #define MSR_CLK_DUTY 0x0 #define MSR_CLK_REG0 0x4 #define MSR_CLK_REG1 0x8 @@ -360,6 +362,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int val; int ret; + ret = mutex_lock_interruptible(&measure_lock); + if (ret) + return ret; + regmap_write(priv->regmap, MSR_CLK_REG0, 0); /* Set measurement duration */ @@ -377,8 +383,10 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, ret = regmap_read_poll_timeout(priv->regmap, MSR_CLK_REG0, val, !(val & MSR_BUSY), 10, 10000); - if (ret) + if (ret) { + mutex_unlock(&measure_lock); return ret; + } /* Disable */ regmap_update_bits(priv->regmap, MSR_CLK_REG0, MSR_ENABLE, 0); @@ -386,6 +394,8 @@ static int meson_measure_id(struct meson_msr_id *clk_msr_id, /* Get the value in multiple of gate time counts */ regmap_read(priv->regmap, MSR_CLK_REG2, &val); + mutex_unlock(&measure_lock); + if (val >= MSR_VAL_MASK) return -EINVAL; From c33b2777d01eb0039a53f14f8c0e4cca8df501c7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 29 Jul 2019 15:02:18 +0200 Subject: [PATCH 025/123] soc: amlogic: meson-clk-measure: add G12B second cluster cpu clk Add the G12B second CPU cluster CPU and SYS_PLL measure IDs. These IDs returns 0Hz on G12A. Reviewed-by: Kevin Hilman Reviewed-by: Martin Blumenstingl Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index c470e24f1dfa..f09b404b39d3 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -324,6 +324,8 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(84, "co_tx"), CLK_MSR_ID(89, "hdmi_todig"), CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "sys_cpub_div16"), + CLK_MSR_ID(92, "sys_pll_cpub_div16"), CLK_MSR_ID(94, "eth_phy_rx"), CLK_MSR_ID(95, "eth_phy_pll"), CLK_MSR_ID(96, "vpu_b"), From 0baf212eab4daf8e4882afd624e1403846418610 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Wed, 31 Jul 2019 14:39:55 +0200 Subject: [PATCH 026/123] soc: amlogic: meson-gx-socinfo: add A311D id Add the SoC ID for the A311D Amlogic SoC. Signed-off-by: Christian Hewitt Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index bca34954518e..ff86a75939e8 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -65,6 +65,7 @@ static const struct meson_gx_package_id { { "S905D2", 0x28, 0x10, 0xf0 }, { "S905X2", 0x28, 0x40, 0xf0 }, { "S922X", 0x29, 0x40, 0xf0 }, + { "A311D", 0x29, 0x10, 0xf0 }, }; static inline unsigned int socinfo_to_major(u32 socinfo) From 9aebf4de220344e2f03ae6386272bf98f80fd295 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 24 Jul 2019 04:05:11 +0530 Subject: [PATCH 027/123] base: soc: Add serial_number attribute to soc Add new attribute named "serial_number" as a standard interface for user space to acquire the serial number of the device. For ST-Ericsson SoCs this is exposed by the cryptically named "soc_id" attribute, but this provides a human readable standardized name for this property. Tested-by: Vinod Koul Signed-off-by: Bjorn Andersson Signed-off-by: Vaishali Thakkar Reviewed-by: Greg Kroah-Hartman Reviewed-by: Stephen Boyd Reviewed-by: Vinod Koul Signed-off-by: Bjorn Andersson --- Documentation/ABI/testing/sysfs-devices-soc | 7 +++++++ drivers/base/soc.c | 7 +++++++ include/linux/sys_soc.h | 1 + 3 files changed, 15 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-devices-soc b/Documentation/ABI/testing/sysfs-devices-soc index 6d9cc253f2b2..ba3a3fac0ee1 100644 --- a/Documentation/ABI/testing/sysfs-devices-soc +++ b/Documentation/ABI/testing/sysfs-devices-soc @@ -26,6 +26,13 @@ Description: Read-only attribute common to all SoCs. Contains SoC family name (e.g. DB8500). +What: /sys/devices/socX/serial_number +Date: January 2019 +contact: Bjorn Andersson +Description: + Read-only attribute supported by most SoCs. Contains the SoC's + serial number, if available. + What: /sys/devices/socX/soc_id Date: January 2012 contact: Lee Jones diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 10b280f30217..b0933b9fe67f 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -33,6 +33,7 @@ static struct bus_type soc_bus_type = { static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(serial_number, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); @@ -57,6 +58,9 @@ static umode_t soc_attribute_mode(struct kobject *kobj, if ((attr == &dev_attr_revision.attr) && (soc_dev->attr->revision != NULL)) return attr->mode; + if ((attr == &dev_attr_serial_number.attr) + && (soc_dev->attr->serial_number != NULL)) + return attr->mode; if ((attr == &dev_attr_soc_id.attr) && (soc_dev->attr->soc_id != NULL)) return attr->mode; @@ -77,6 +81,8 @@ static ssize_t soc_info_get(struct device *dev, return sprintf(buf, "%s\n", soc_dev->attr->family); if (attr == &dev_attr_revision) return sprintf(buf, "%s\n", soc_dev->attr->revision); + if (attr == &dev_attr_serial_number) + return sprintf(buf, "%s\n", soc_dev->attr->serial_number); if (attr == &dev_attr_soc_id) return sprintf(buf, "%s\n", soc_dev->attr->soc_id); @@ -87,6 +93,7 @@ static ssize_t soc_info_get(struct device *dev, static struct attribute *soc_attr[] = { &dev_attr_machine.attr, &dev_attr_family.attr, + &dev_attr_serial_number.attr, &dev_attr_soc_id.attr, &dev_attr_revision.attr, NULL, diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h index b7c70c3e953f..48ceea867dd6 100644 --- a/include/linux/sys_soc.h +++ b/include/linux/sys_soc.h @@ -12,6 +12,7 @@ struct soc_device_attribute { const char *machine; const char *family; const char *revision; + const char *serial_number; const char *soc_id; const void *data; }; From f7ccc7a397cf2ef64aebb2f726970b93203858d2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 24 Jul 2019 04:05:12 +0530 Subject: [PATCH 028/123] base: soc: Export soc_device_register/unregister APIs Qcom Socinfo driver can be built as a module, so export these two APIs. Tested-by: Vinod Koul Signed-off-by: Vinod Koul Signed-off-by: Vaishali Thakkar Reviewed-by: Greg Kroah-Hartman Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson --- drivers/base/soc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/soc.c b/drivers/base/soc.c index b0933b9fe67f..7c0c5ca5953d 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -164,6 +164,7 @@ out2: out1: return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(soc_device_register); /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ void soc_device_unregister(struct soc_device *soc_dev) @@ -173,6 +174,7 @@ void soc_device_unregister(struct soc_device *soc_dev) device_unregister(&soc_dev->dev); early_soc_dev_attr = NULL; } +EXPORT_SYMBOL_GPL(soc_device_unregister); static int __init soc_bus_register(void) { From efb448d0a3fca01bb987dd70963da6185b81751e Mon Sep 17 00:00:00 2001 From: Imran Khan Date: Wed, 24 Jul 2019 04:05:13 +0530 Subject: [PATCH 029/123] soc: qcom: Add socinfo driver The Qualcomm socinfo driver exposes information about the SoC, its version and its serial number to user space. Tested-by: Vinod Koul Reviewed-by: Vinod Koul Signed-off-by: Imran Khan [Bjorn: Extract code to platform_driver, split patch in multiple] Signed-off-by: Bjorn Andersson [Vaishali: Simplify declarations, introduce qcom_socinfo struct, Fix memory leak, Remove extra code and Misc code refactoring] Signed-off-by: Vaishali Thakkar Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 8 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/smem.c | 9 ++ drivers/soc/qcom/socinfo.c | 194 +++++++++++++++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 drivers/soc/qcom/socinfo.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a6d1bfb17279..661e47acc354 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -175,6 +175,14 @@ config QCOM_SMSM Say yes here to support the Qualcomm Shared Memory State Machine. The state machine is represented by bits in shared memory. +config QCOM_SOCINFO + tristate "Qualcomm socinfo driver" + depends on QCOM_SMEM + select SOC_BUS + help + Say yes here to support the Qualcomm socinfo driver, providing + information about the SoC to user space. + config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on ARCH_QCOM || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index eeb088beb15f..162788701a77 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o +obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index f27c00d82ae4..0caf67e2f037 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -268,6 +268,7 @@ struct qcom_smem { struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; u32 item_count; + struct platform_device *socinfo; unsigned num_regions; struct smem_region regions[]; @@ -963,11 +964,19 @@ static int qcom_smem_probe(struct platform_device *pdev) __smem = smem; + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", + PLATFORM_DEVID_NONE, NULL, + 0); + if (IS_ERR(smem->socinfo)) + dev_dbg(&pdev->dev, "failed to register socinfo device\n"); + return 0; } static int qcom_smem_remove(struct platform_device *pdev) { + platform_device_unregister(__smem->socinfo); + hwspin_lock_free(__smem->hwlock); __smem = NULL; diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c new file mode 100644 index 000000000000..1b9eb44df7fe --- /dev/null +++ b/drivers/soc/qcom/socinfo.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * SoC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. + */ +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define SOCINFO_MINOR(ver) ((ver) & 0xffff) + +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 + +/* + * SMEM item id, used to acquire handles to respective + * SMEM region. + */ +#define SMEM_HW_SW_BUILD_ID 137 + +/* Socinfo SMEM item structure */ +struct socinfo { + __le32 fmt; + __le32 id; + __le32 ver; + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; + /* Version 2 */ + __le32 raw_id; + __le32 raw_ver; + /* Version 3 */ + __le32 hw_plat; + /* Version 4 */ + __le32 plat_ver; + /* Version 5 */ + __le32 accessory_chip; + /* Version 6 */ + __le32 hw_plat_subtype; + /* Version 7 */ + __le32 pmic_model; + __le32 pmic_die_rev; + /* Version 8 */ + __le32 pmic_model_1; + __le32 pmic_die_rev_1; + __le32 pmic_model_2; + __le32 pmic_die_rev_2; + /* Version 9 */ + __le32 foundry_id; + /* Version 10 */ + __le32 serial_num; + /* Version 11 */ + __le32 num_pmics; + __le32 pmic_array_offset; + /* Version 12 */ + __le32 chip_family; + __le32 raw_device_family; + __le32 raw_device_num; +}; + +struct qcom_socinfo { + struct soc_device *soc_dev; + struct soc_device_attribute attr; +}; + +struct soc_id { + unsigned int id; + const char *name; +}; + +static const struct soc_id soc_id[] = { + { 87, "MSM8960" }, + { 109, "APQ8064" }, + { 122, "MSM8660A" }, + { 123, "MSM8260A" }, + { 124, "APQ8060A" }, + { 126, "MSM8974" }, + { 130, "MPQ8064" }, + { 138, "MSM8960AB" }, + { 139, "APQ8060AB" }, + { 140, "MSM8260AB" }, + { 141, "MSM8660AB" }, + { 178, "APQ8084" }, + { 184, "APQ8074" }, + { 185, "MSM8274" }, + { 186, "MSM8674" }, + { 194, "MSM8974PRO" }, + { 206, "MSM8916" }, + { 208, "APQ8074-AA" }, + { 209, "APQ8074-AB" }, + { 210, "APQ8074PRO" }, + { 211, "MSM8274-AA" }, + { 212, "MSM8274-AB" }, + { 213, "MSM8274PRO" }, + { 214, "MSM8674-AA" }, + { 215, "MSM8674-AB" }, + { 216, "MSM8674PRO" }, + { 217, "MSM8974-AA" }, + { 218, "MSM8974-AB" }, + { 246, "MSM8996" }, + { 247, "APQ8016" }, + { 248, "MSM8216" }, + { 249, "MSM8116" }, + { 250, "MSM8616" }, + { 291, "APQ8096" }, + { 305, "MSM8996SG" }, + { 310, "MSM8996AU" }, + { 311, "APQ8096AU" }, + { 312, "APQ8096SG" }, +}; + +static const char *socinfo_machine(struct device *dev, unsigned int id) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(soc_id); idx++) { + if (soc_id[idx].id == id) + return soc_id[idx].name; + } + + return NULL; +} + +static int qcom_socinfo_probe(struct platform_device *pdev) +{ + struct qcom_socinfo *qs; + struct socinfo *info; + size_t item_size; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, + &item_size); + if (IS_ERR(info)) { + dev_err(&pdev->dev, "Couldn't find socinfo\n"); + return PTR_ERR(info); + } + + qs = devm_kzalloc(&pdev->dev, sizeof(*qs), GFP_KERNEL); + if (!qs) + return -ENOMEM; + + qs->attr.family = "Snapdragon"; + qs->attr.machine = socinfo_machine(&pdev->dev, + le32_to_cpu(info->id)); + qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u", + SOCINFO_MAJOR(le32_to_cpu(info->ver)), + SOCINFO_MINOR(le32_to_cpu(info->ver))); + if (offsetof(struct socinfo, serial_num) <= item_size) + qs->attr.serial_number = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%u", + le32_to_cpu(info->serial_num)); + + qs->soc_dev = soc_device_register(&qs->attr); + if (IS_ERR(qs->soc_dev)) + return PTR_ERR(qs->soc_dev); + + /* Feed the soc specific unique data into entropy pool */ + add_device_randomness(info, item_size); + + platform_set_drvdata(pdev, qs->soc_dev); + + return 0; +} + +static int qcom_socinfo_remove(struct platform_device *pdev) +{ + struct qcom_socinfo *qs = platform_get_drvdata(pdev); + + soc_device_unregister(qs->soc_dev); + + return 0; +} + +static struct platform_driver qcom_socinfo_driver = { + .probe = qcom_socinfo_probe, + .remove = qcom_socinfo_remove, + .driver = { + .name = "qcom-socinfo", + }, +}; + +module_platform_driver(qcom_socinfo_driver); + +MODULE_DESCRIPTION("Qualcomm SoCinfo driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-socinfo"); From 9c84c1e78634bd7f93541ad9ad88e99b21495da7 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Wed, 24 Jul 2019 04:05:14 +0530 Subject: [PATCH 030/123] soc: qcom: socinfo: Expose custom attributes The Qualcomm socinfo provides a number of additional attributes, add these to the socinfo driver and expose them via debugfs functionality. Tested-by: Vinod Koul Reviewed-by: Vinod Koul Signed-off-by: Vaishali Thakkar Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 190 +++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 1b9eb44df7fe..6a4795433d57 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -4,6 +4,7 @@ * Copyright (c) 2017-2019, Linaro Ltd. */ +#include #include #include #include @@ -20,6 +21,7 @@ */ #define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) #define SOCINFO_MINOR(ver) ((ver) & 0xffff) +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) #define SMEM_SOCINFO_BUILD_ID_LENGTH 32 @@ -29,6 +31,27 @@ */ #define SMEM_HW_SW_BUILD_ID 137 +#ifdef CONFIG_DEBUG_FS +static const char *const pmic_models[] = { + [0] = "Unknown PMIC model", + [9] = "PM8994", + [11] = "PM8916", + [13] = "PM8058", + [14] = "PM8028", + [15] = "PM8901", + [16] = "PM8027", + [17] = "ISL9519", + [18] = "PM8921", + [19] = "PM8018", + [20] = "PM8015", + [21] = "PM8014", + [22] = "PM8821", + [23] = "PM8038", + [24] = "PM8922", + [25] = "PM8917", +}; +#endif /* CONFIG_DEBUG_FS */ + /* Socinfo SMEM item structure */ struct socinfo { __le32 fmt; @@ -67,9 +90,28 @@ struct socinfo { __le32 raw_device_num; }; +#ifdef CONFIG_DEBUG_FS +struct socinfo_params { + u32 raw_device_family; + u32 hw_plat_subtype; + u32 accessory_chip; + u32 raw_device_num; + u32 chip_family; + u32 foundry_id; + u32 plat_ver; + u32 raw_ver; + u32 hw_plat; + u32 fmt; +}; +#endif /* CONFIG_DEBUG_FS */ + struct qcom_socinfo { struct soc_device *soc_dev; struct soc_device_attribute attr; +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_root; + struct socinfo_params info; +#endif /* CONFIG_DEBUG_FS */ }; struct soc_id { @@ -130,6 +172,150 @@ static const char *socinfo_machine(struct device *dev, unsigned int id) return NULL; } +#ifdef CONFIG_DEBUG_FS + +#define QCOM_OPEN(name, _func) \ +static int qcom_open_##name(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, _func, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_ ##name## _ops = { \ + .open = qcom_open_##name, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +#define DEBUGFS_ADD(info, name) \ + debugfs_create_file(__stringify(name), 0400, \ + qcom_socinfo->dbg_root, \ + info, &qcom_ ##name## _ops) + + +static int qcom_show_build_id(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%s\n", socinfo->build_id); + + return 0; +} + +static int qcom_show_pmic_model(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + int model = SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_model)); + + if (model < 0) + return -EINVAL; + + seq_printf(seq, "%s\n", pmic_models[model]); + + return 0; +} + +static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%u.%u\n", + SOCINFO_MAJOR(le32_to_cpu(socinfo->pmic_die_rev)), + SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_die_rev))); + + return 0; +} + +QCOM_OPEN(build_id, qcom_show_build_id); +QCOM_OPEN(pmic_model, qcom_show_pmic_model); +QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision); + +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ + size_t size; + + qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL); + + qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt); + + switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 12): + qcom_socinfo->info.chip_family = + __le32_to_cpu(info->chip_family); + qcom_socinfo->info.raw_device_family = + __le32_to_cpu(info->raw_device_family); + qcom_socinfo->info.raw_device_num = + __le32_to_cpu(info->raw_device_num); + + debugfs_create_x32("chip_family", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.chip_family); + debugfs_create_x32("raw_device_family", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_family); + debugfs_create_x32("raw_device_number", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_num); + case SOCINFO_VERSION(0, 11): + case SOCINFO_VERSION(0, 10): + case SOCINFO_VERSION(0, 9): + qcom_socinfo->info.foundry_id = __le32_to_cpu(info->foundry_id); + + debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.foundry_id); + case SOCINFO_VERSION(0, 8): + case SOCINFO_VERSION(0, 7): + DEBUGFS_ADD(info, pmic_model); + DEBUGFS_ADD(info, pmic_die_rev); + case SOCINFO_VERSION(0, 6): + qcom_socinfo->info.hw_plat_subtype = + __le32_to_cpu(info->hw_plat_subtype); + + debugfs_create_u32("hardware_platform_subtype", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat_subtype); + case SOCINFO_VERSION(0, 5): + qcom_socinfo->info.accessory_chip = + __le32_to_cpu(info->accessory_chip); + + debugfs_create_u32("accessory_chip", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.accessory_chip); + case SOCINFO_VERSION(0, 4): + qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver); + + debugfs_create_u32("platform_version", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.plat_ver); + case SOCINFO_VERSION(0, 3): + qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat); + + debugfs_create_u32("hardware_platform", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat); + case SOCINFO_VERSION(0, 2): + qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver); + + debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_ver); + case SOCINFO_VERSION(0, 1): + DEBUGFS_ADD(info, build_id); + break; + } +} + +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) +{ + debugfs_remove_recursive(qcom_socinfo->dbg_root); +} +#else +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ +} +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) { } +#endif /* CONFIG_DEBUG_FS */ + static int qcom_socinfo_probe(struct platform_device *pdev) { struct qcom_socinfo *qs; @@ -162,6 +348,8 @@ static int qcom_socinfo_probe(struct platform_device *pdev) if (IS_ERR(qs->soc_dev)) return PTR_ERR(qs->soc_dev); + socinfo_debugfs_init(qs, info); + /* Feed the soc specific unique data into entropy pool */ add_device_randomness(info, item_size); @@ -176,6 +364,8 @@ static int qcom_socinfo_remove(struct platform_device *pdev) soc_device_unregister(qs->soc_dev); + socinfo_debugfs_exit(qs); + return 0; } From cd23d1405be666a7c43045abe339bc1acaa3400b Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Wed, 24 Jul 2019 04:05:15 +0530 Subject: [PATCH 031/123] soc: qcom: socinfo: Expose image information The socinfo driver provides information about version of the various images loaded in the system. Expose this to user space for debugging purpose. Tested-by: Vinod Koul Reviewed-by: Vinod Koul Signed-off-by: Vaishali Thakkar Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 6a4795433d57..855353bed19e 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -32,6 +32,39 @@ #define SMEM_HW_SW_BUILD_ID 137 #ifdef CONFIG_DEBUG_FS +#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32 +#define SMEM_IMAGE_VERSION_SIZE 4096 +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 +#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20 +#define SMEM_IMAGE_VERSION_OEM_SIZE 32 + +/* + * SMEM Image table indices + */ +#define SMEM_IMAGE_TABLE_BOOT_INDEX 0 +#define SMEM_IMAGE_TABLE_TZ_INDEX 1 +#define SMEM_IMAGE_TABLE_RPM_INDEX 3 +#define SMEM_IMAGE_TABLE_APPS_INDEX 10 +#define SMEM_IMAGE_TABLE_MPSS_INDEX 11 +#define SMEM_IMAGE_TABLE_ADSP_INDEX 12 +#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 +#define SMEM_IMAGE_TABLE_VIDEO_INDEX 14 +#define SMEM_IMAGE_VERSION_TABLE 469 + +/* + * SMEM Image table names + */ +static const char *const socinfo_image_names[] = { + [SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp", + [SMEM_IMAGE_TABLE_APPS_INDEX] = "apps", + [SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot", + [SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss", + [SMEM_IMAGE_TABLE_MPSS_INDEX] = "mpss", + [SMEM_IMAGE_TABLE_RPM_INDEX] = "rpm", + [SMEM_IMAGE_TABLE_TZ_INDEX] = "tz", + [SMEM_IMAGE_TABLE_VIDEO_INDEX] = "video", +}; + static const char *const pmic_models[] = { [0] = "Unknown PMIC model", [9] = "PM8994", @@ -103,6 +136,13 @@ struct socinfo_params { u32 hw_plat; u32 fmt; }; + +struct smem_image_version { + char name[SMEM_IMAGE_VERSION_NAME_SIZE]; + char variant[SMEM_IMAGE_VERSION_VARIANT_SIZE]; + char pad; + char oem[SMEM_IMAGE_VERSION_OEM_SIZE]; +}; #endif /* CONFIG_DEBUG_FS */ struct qcom_socinfo { @@ -230,10 +270,37 @@ QCOM_OPEN(build_id, qcom_show_build_id); QCOM_OPEN(pmic_model, qcom_show_pmic_model); QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision); +#define DEFINE_IMAGE_OPS(type) \ +static int show_image_##type(struct seq_file *seq, void *p) \ +{ \ + struct smem_image_version *image_version = seq->private; \ + seq_puts(seq, image_version->type); \ + seq_puts(seq, "\n"); \ + return 0; \ +} \ +static int open_image_##type(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, show_image_##type, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_image_##type##_ops = { \ + .open = open_image_##type, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +DEFINE_IMAGE_OPS(name); +DEFINE_IMAGE_OPS(variant); +DEFINE_IMAGE_OPS(oem); + static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, struct socinfo *info) { + struct smem_image_version *versions; + struct dentry *dentry; size_t size; + int i; qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL); @@ -302,6 +369,23 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, DEBUGFS_ADD(info, build_id); break; } + + versions = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE, + &size); + + for (i = 0; i < ARRAY_SIZE(socinfo_image_names); i++) { + if (!socinfo_image_names[i]) + continue; + + dentry = debugfs_create_dir(socinfo_image_names[i], + qcom_socinfo->dbg_root); + debugfs_create_file("name", 0400, dentry, &versions[i], + &qcom_image_name_ops); + debugfs_create_file("variant", 0400, dentry, &versions[i], + &qcom_image_variant_ops); + debugfs_create_file("oem", 0400, dentry, &versions[i], + &qcom_image_oem_ops); + } } static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) From 05589b30b21ac0273970b61edd50c07d2ba156af Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Tue, 30 Jul 2019 11:24:42 -0400 Subject: [PATCH 032/123] soc: qcom: Extend AOSS QMP driver to support resources that are used to wake up the SoC. The AOSS QMP driver is extended to communicate with the additional resources. These resources are then registered as cooling devices with the thermal framework. Signed-off-by: Thara Gopinath Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 131 +++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 5f885196f4d0..443dab2207de 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 @@ -40,6 +42,17 @@ /* 64 bytes is enough to store the requests and provides padding to 4 bytes */ #define QMP_MSG_LEN 64 +#define QMP_NUM_COOLING_RESOURCES 2 + +static bool qmp_cdev_init_state = 1; + +struct qmp_cooling_device { + struct thermal_cooling_device *cdev; + struct qmp *qmp; + char *name; + bool state; +}; + /** * struct qmp - driver state for QMP implementation * @msgram: iomem referencing the message RAM used for communication @@ -69,6 +82,7 @@ struct qmp { struct clk_hw qdss_clk; struct genpd_onecell_data pd_data; + struct qmp_cooling_device *cooling_devs; }; struct qmp_pd { @@ -385,6 +399,118 @@ static void qmp_pd_remove(struct qmp *qmp) pm_genpd_remove(data->domains[i]); } +static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = qmp_cdev_init_state; + return 0; +} + +static int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + + *state = qmp_cdev->state; + return 0; +} + +static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + char buf[QMP_MSG_LEN] = {}; + bool cdev_state; + int ret; + + /* Normalize state */ + cdev_state = !!state; + + if (qmp_cdev->state == state) + return 0; + + snprintf(buf, sizeof(buf), + "{class: volt_flr, event:zero_temp, res:%s, value:%s}", + qmp_cdev->name, + cdev_state ? "off" : "on"); + + ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf)); + + if (!ret) + qmp_cdev->state = cdev_state; + + return ret; +} + +static struct thermal_cooling_device_ops qmp_cooling_device_ops = { + .get_max_state = qmp_cdev_get_max_state, + .get_cur_state = qmp_cdev_get_cur_state, + .set_cur_state = qmp_cdev_set_cur_state, +}; + +static int qmp_cooling_device_add(struct qmp *qmp, + struct qmp_cooling_device *qmp_cdev, + struct device_node *node) +{ + char *cdev_name = (char *)node->name; + + qmp_cdev->qmp = qmp; + qmp_cdev->state = qmp_cdev_init_state; + qmp_cdev->name = cdev_name; + qmp_cdev->cdev = devm_thermal_of_cooling_device_register + (qmp->dev, node, + cdev_name, + qmp_cdev, &qmp_cooling_device_ops); + + if (IS_ERR(qmp_cdev->cdev)) + dev_err(qmp->dev, "unable to register %s cooling device\n", + cdev_name); + + return PTR_ERR_OR_ZERO(qmp_cdev->cdev); +} + +static int qmp_cooling_devices_register(struct qmp *qmp) +{ + struct device_node *np, *child; + int count = QMP_NUM_COOLING_RESOURCES; + int ret; + + np = qmp->dev->of_node; + + qmp->cooling_devs = devm_kcalloc(qmp->dev, count, + sizeof(*qmp->cooling_devs), + GFP_KERNEL); + + if (!qmp->cooling_devs) + return -ENOMEM; + + for_each_available_child_of_node(np, child) { + if (!of_find_property(child, "#cooling-cells", NULL)) + continue; + ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++], + child); + if (ret) + goto unroll; + } + + return 0; + +unroll: + while (--count >= 0) + thermal_cooling_device_unregister + (qmp->cooling_devs[count].cdev); + + return ret; +} + +static void qmp_cooling_devices_remove(struct qmp *qmp) +{ + int i; + + for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++) + thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev); +} + static int qmp_probe(struct platform_device *pdev) { struct resource *res; @@ -433,6 +559,10 @@ static int qmp_probe(struct platform_device *pdev) if (ret) goto err_remove_qdss_clk; + ret = qmp_cooling_devices_register(qmp); + if (ret) + dev_err(&pdev->dev, "failed to register aoss cooling devices\n"); + platform_set_drvdata(pdev, qmp); return 0; @@ -453,6 +583,7 @@ static int qmp_remove(struct platform_device *pdev) qmp_qdss_clk_remove(qmp); qmp_pd_remove(qmp); + qmp_cooling_devices_remove(qmp); qmp_close(qmp); mbox_free_channel(qmp->mbox_chan); From 9d4db2f5f0e6dc4c0dfb94110fe488e9eb191748 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 11:15:02 -0700 Subject: [PATCH 033/123] bus: sunxi-rsb: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Greg Kroah-Hartman Cc: Maxime Ripard Cc: Chen-Yu Tsai Signed-off-by: Stephen Boyd Signed-off-by: Chen-Yu Tsai --- drivers/bus/sunxi-rsb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 1b76d9585902..be79d6c6a4e4 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -651,10 +651,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev) return PTR_ERR(rsb->regs); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "failed to retrieve irq: %d\n", irq); + if (irq < 0) return irq; - } rsb->clk = devm_clk_get(dev, NULL); if (IS_ERR(rsb->clk)) { From e7ef681a8ab8568100eedac1d50fe5973178147b Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Tue, 23 Jul 2019 17:17:27 +0200 Subject: [PATCH 034/123] dt-bindings: Document the DesignWare IP reset bindings This adds documentation of device tree bindings for the DesignWare IP reset controller. Signed-off-by: Gustavo Pimentel Signed-off-by: Luis Oliveira Reviewed-by: Rob Herring Signed-off-by: Philipp Zabel --- .../bindings/reset/snps,dw-reset.txt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/reset/snps,dw-reset.txt diff --git a/Documentation/devicetree/bindings/reset/snps,dw-reset.txt b/Documentation/devicetree/bindings/reset/snps,dw-reset.txt new file mode 100644 index 000000000000..f94f911dd98d --- /dev/null +++ b/Documentation/devicetree/bindings/reset/snps,dw-reset.txt @@ -0,0 +1,30 @@ +Synopsys DesignWare Reset controller +======================================= + +Please also refer to reset.txt in this directory for common reset +controller binding usage. + +Required properties: + +- compatible: should be one of the following. + "snps,dw-high-reset" - for active high configuration + "snps,dw-low-reset" - for active low configuration + +- reg: physical base address of the controller and length of memory mapped + region. + +- #reset-cells: must be 1. + +example: + + dw_rst_1: reset-controller@0000 { + compatible = "snps,dw-high-reset"; + reg = <0x0000 0x4>; + #reset-cells = <1>; + }; + + dw_rst_2: reset-controller@1000 {i + compatible = "snps,dw-low-reset"; + reg = <0x1000 0x8>; + #reset-cells = <1>; + }; From ea651ffd4f7febd4a0b3fdb98e39a6980e978a2c Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Tue, 23 Jul 2019 17:17:28 +0200 Subject: [PATCH 035/123] reset: Add DesignWare IP support to simple reset The reset-simple driver can be now used on DesignWare IPs by default by selecting the following compatible strings: - snps,dw-high-reset for active high resets inputs - snps,dw-low-reset for active low resets inputs Signed-off-by: Gustavo Pimentel Signed-off-by: Luis Oliveira Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 2 +- drivers/reset/reset-simple.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 21efb7d39d62..6d5d76db55b0 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -118,7 +118,7 @@ config RESET_QCOM_PDC config RESET_SIMPLE bool "Simple Reset Controller Driver" if COMPILE_TEST - default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN + default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN || ARC help This enables a simple reset controller driver for reset lines that that can be asserted and deasserted by toggling bits in a contiguous, diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 1154f7b1f4dd..067e7e7b34f1 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -127,6 +127,9 @@ static const struct of_device_id reset_simple_dt_ids[] = { { .compatible = "aspeed,ast2500-lpc-reset" }, { .compatible = "bitmain,bm1880-reset", .data = &reset_simple_active_low }, + { .compatible = "snps,dw-high-reset" }, + { .compatible = "snps,dw-low-reset", + .data = &reset_simple_active_low }, { /* sentinel */ }, }; From 7bea41c4a25675a32bc3a63cfbd98099fdfb2695 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 6 Aug 2019 19:23:56 -0700 Subject: [PATCH 036/123] soc: qcom: socinfo: Annotate switch cases with fall through Introduce fall through annotations in the switch statements of socinfo_debugfs_init() to silence compiler warnings. Acked-by: Vaishali Thakkar Fixes: 9c84c1e78634 ("soc: qcom: socinfo: Expose custom attributes") Reported-by: Stephen Rothwell Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 855353bed19e..a39ea5061dc5 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -323,6 +323,7 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, debugfs_create_x32("raw_device_number", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.raw_device_num); + /* Fall through */ case SOCINFO_VERSION(0, 11): case SOCINFO_VERSION(0, 10): case SOCINFO_VERSION(0, 9): @@ -330,10 +331,12 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.foundry_id); + /* Fall through */ case SOCINFO_VERSION(0, 8): case SOCINFO_VERSION(0, 7): DEBUGFS_ADD(info, pmic_model); DEBUGFS_ADD(info, pmic_die_rev); + /* Fall through */ case SOCINFO_VERSION(0, 6): qcom_socinfo->info.hw_plat_subtype = __le32_to_cpu(info->hw_plat_subtype); @@ -341,6 +344,7 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, debugfs_create_u32("hardware_platform_subtype", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.hw_plat_subtype); + /* Fall through */ case SOCINFO_VERSION(0, 5): qcom_socinfo->info.accessory_chip = __le32_to_cpu(info->accessory_chip); @@ -348,23 +352,27 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, debugfs_create_u32("accessory_chip", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.accessory_chip); + /* Fall through */ case SOCINFO_VERSION(0, 4): qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver); debugfs_create_u32("platform_version", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.plat_ver); + /* Fall through */ case SOCINFO_VERSION(0, 3): qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat); debugfs_create_u32("hardware_platform", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.hw_plat); + /* Fall through */ case SOCINFO_VERSION(0, 2): qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver); debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root, &qcom_socinfo->info.raw_ver); + /* Fall through */ case SOCINFO_VERSION(0, 1): DEBUGFS_ADD(info, build_id); break; From 4752544a447b52b9949eb648a3b7719357853f91 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 23 Jul 2019 14:00:09 +0100 Subject: [PATCH 037/123] firmware: arm_scmi: Use the correct style for SPDX License Identifier Fix to correct the SPDX License Identifier style in header file related to firmware frivers for ARM SCMI message protocol. For C header files Documentation/process/license-rules.rst mandates C-like comments(opposed to C source files where C++ style should be used). While at it, change GPL-2.0 to GPL-2.0-only similar to the ones in psci.h and scpi_protocol.h Signed-off-by: Sudeep Holla --- include/linux/scmi_protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9ff2e9357e9a..aa1e791779b4 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0-only */ /* * SCMI Message Protocol driver header * From 9eefa43a1a03960c7458e1463132f893702741be Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:33 +0100 Subject: [PATCH 038/123] firmware: arm_scmi: Align few names in sensors protocol with SCMI specification Looks like more code developed during the draft versions of the specification slipped through and they don't match the final released version. This seem to have happened only with sensor protocol. Renaming few command and function names here to match exactly with the released version of SCMI specification for ease of maintenance. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 28 +++++++++++++++------------- include/linux/scmi_protocol.h | 12 ++++++------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 0e94ab56f679..17dbabd8a94a 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -9,8 +9,8 @@ enum scmi_sensor_protocol_cmd { SENSOR_DESCRIPTION_GET = 0x3, - SENSOR_CONFIG_SET = 0x4, - SENSOR_TRIP_POINT_SET = 0x5, + SENSOR_TRIP_POINT_NOTIFY = 0x4, + SENSOR_TRIP_POINT_CONFIG = 0x5, SENSOR_READING_GET = 0x6, }; @@ -42,9 +42,10 @@ struct scmi_msg_resp_sensor_description { } desc[0]; }; -struct scmi_msg_set_sensor_config { +struct scmi_msg_sensor_trip_point_notify { __le32 id; __le32 event_control; +#define SENSOR_TP_NOTIFY_ALL BIT(0) }; struct scmi_msg_set_sensor_trip_point { @@ -160,15 +161,15 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, return ret; } -static int -scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id) +static int scmi_sensor_trip_point_notify(const struct scmi_handle *handle, + u32 sensor_id, bool enable) { int ret; - u32 evt_cntl = BIT(0); + u32 evt_cntl = enable ? SENSOR_TP_NOTIFY_ALL : 0; struct scmi_xfer *t; - struct scmi_msg_set_sensor_config *cfg; + struct scmi_msg_sensor_trip_point_notify *cfg; - ret = scmi_xfer_get_init(handle, SENSOR_CONFIG_SET, + ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_NOTIFY, SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t); if (ret) return ret; @@ -183,15 +184,16 @@ scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id) return ret; } -static int scmi_sensor_trip_point_set(const struct scmi_handle *handle, - u32 sensor_id, u8 trip_id, u64 trip_value) +static int +scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id, + u8 trip_id, u64 trip_value) { int ret; u32 evt_cntl = SENSOR_TP_BOTH; struct scmi_xfer *t; struct scmi_msg_set_sensor_trip_point *trip; - ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_SET, + ret = scmi_xfer_get_init(handle, SENSOR_TRIP_POINT_CONFIG, SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t); if (ret) return ret; @@ -255,8 +257,8 @@ static int scmi_sensor_count_get(const struct scmi_handle *handle) static struct scmi_sensor_ops sensor_ops = { .count_get = scmi_sensor_count_get, .info_get = scmi_sensor_info_get, - .configuration_set = scmi_sensor_configuration_set, - .trip_point_set = scmi_sensor_trip_point_set, + .trip_point_notify = scmi_sensor_trip_point_notify, + .trip_point_config = scmi_sensor_trip_point_config, .reading_get = scmi_sensor_reading_get, }; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index aa1e791779b4..1383d47e6435 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -167,9 +167,9 @@ enum scmi_sensor_class { * * @count_get: get the count of sensors provided by SCMI * @info_get: get the information of the specified sensor - * @configuration_set: control notifications on cross-over events for + * @trip_point_notify: control notifications on cross-over events for * the trip-points - * @trip_point_set: selects and configures a trip-point of interest + * @trip_point_config: selects and configures a trip-point of interest * @reading_get: gets the current value of the sensor */ struct scmi_sensor_ops { @@ -177,10 +177,10 @@ struct scmi_sensor_ops { const struct scmi_sensor_info *(*info_get) (const struct scmi_handle *handle, u32 sensor_id); - int (*configuration_set)(const struct scmi_handle *handle, - u32 sensor_id); - int (*trip_point_set)(const struct scmi_handle *handle, u32 sensor_id, - u8 trip_id, u64 trip_value); + int (*trip_point_notify)(const struct scmi_handle *handle, + u32 sensor_id, bool enable); + int (*trip_point_config)(const struct scmi_handle *handle, + u32 sensor_id, u8 trip_id, u64 trip_value); int (*reading_get)(const struct scmi_handle *handle, u32 sensor_id, bool async, u64 *value); }; From 37bbffcb19a7c98ed0a8c0b2dd2e11bfc05d5964 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:35 +0100 Subject: [PATCH 039/123] firmware: arm_scmi: Remove extra check for invalid length message responses scmi_xfer_get_init ensures both transmit and receive buffer lengths are within the maximum limits. If receive buffer length is not supplied by the caller, it's set to the maximum limit value. Receive buffer length is never modified after that. So there's no need for the extra check when receive transmit completion for a command essage. Further, if the response header length is greater than the prescribed receive buffer length, the response buffer is truncated to the latter. Reported-by: Jim Quinlan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index b5bc4c7a8fab..6ef652940099 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -230,12 +230,6 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) xfer = &minfo->xfer_block[xfer_id]; scmi_dump_header_dbg(dev, &xfer->hdr); - /* Is the message of valid length? */ - if (xfer->rx.len > info->desc->max_msg_size) { - dev_err(dev, "unable to handle %zu xfer(max %d)\n", - xfer->rx.len, info->desc->max_msg_size); - return; - } scmi_fetch_response(xfer, mem); complete(&xfer->done); From c29a628976b39ecf2f9ced376ceeef8e9f6ccde0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:38 +0100 Subject: [PATCH 040/123] firmware: arm_scmi: Fix few trivial typos in comments While adding new comments found couple of typos that are better fixed. s/informfation/information/ s/statues/status/ Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 6ef652940099..cac255c418b2 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -86,7 +86,7 @@ struct scmi_desc { }; /** - * struct scmi_chan_info - Structure representing a SCMI channel informfation + * struct scmi_chan_info - Structure representing a SCMI channel information * * @cl: Mailbox Client * @chan: Transmit/Receive mailbox channel @@ -190,7 +190,7 @@ static void scmi_fetch_response(struct scmi_xfer *xfer, struct scmi_shared_mem __iomem *mem) { xfer->hdr.status = ioread32(mem->msg_payload); - /* Skip the length of header and statues in payload area i.e 8 bytes*/ + /* Skip the length of header and status in payload area i.e 8 bytes */ xfer->rx.len = min_t(size_t, xfer->rx.len, ioread32(&mem->length) - 8); /* Take a copy to the rx buffer.. */ From 5b65af8f60f580188c0be55b25cd45908024b985 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:40 +0100 Subject: [PATCH 041/123] firmware: arm_scmi: Use the term 'message' instead of 'command' In preparation to adding support for other two types of messages that SCMI specification mentions, let's replace the term 'command' with the correct term 'message'. As per the specification the messages are of 3 types: commands(synchronous or asynchronous), delayed responses and notifications. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/common.h | 10 +++++----- drivers/firmware/arm_scmi/driver.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 44fd4f9404a9..a9eee62c7142 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -48,11 +48,11 @@ struct scmi_msg_resp_prot_version { /** * struct scmi_msg_hdr - Message(Tx/Rx) header * - * @id: The identifier of the command being sent - * @protocol_id: The identifier of the protocol used to send @id command - * @seq: The token to identify the message. when a message/command returns, - * the platform returns the whole message header unmodified including - * the token + * @id: The identifier of the message being sent + * @protocol_id: The identifier of the protocol used to send @id message + * @seq: The token to identify the message. When a message returns, the + * platform returns the whole message header unmodified including the + * token * @status: Status of the transfer once it's complete * @poll_completion: Indicate if the transfer needs to be polled for * completion or interrupt mode is used diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index cac255c418b2..69bf85fea967 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -182,7 +182,7 @@ static inline int scmi_to_linux_errno(int errno) static inline void scmi_dump_header_dbg(struct device *dev, struct scmi_msg_hdr *hdr) { - dev_dbg(dev, "Command ID: %x Sequence ID: %x Protocol: %x\n", + dev_dbg(dev, "Message ID: %x Sequence ID: %x Protocol: %x\n", hdr->id, hdr->seq, hdr->protocol_id); } @@ -241,7 +241,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) * @hdr: pointer to header containing all the information on message id, * protocol id and sequence id. * - * Return: 32-bit packed command header to be sent to the platform. + * Return: 32-bit packed message header to be sent to the platform. */ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) { @@ -280,7 +280,7 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) * * @handle: Pointer to SCMI entity handle * - * Helper function which is used by various command functions that are + * Helper function which is used by various message functions that are * exposed to clients of this driver for allocating a message traffic event. * * This function can sleep depending on pending requests already in the system From 9dc34d635c67e57051853855c43249408641a5ab Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 15:48:36 +0100 Subject: [PATCH 042/123] firmware: arm_scmi: Check if platform has released shmem before using Sometimes platfom may take too long to respond to the command and OS might timeout before platform transfer the ownership of the shared memory region to the OS with the response. Since the mailbox channel associated with the channel is freed and new commands are dispatch on the same channel, OS needs to wait until it gets back the ownership. If not, either OS may end up overwriting the platform response for the last command(which is fine as OS timed out that command) or platform might overwrite the payload for the next command with the response for the old. The latter is problematic as platform may end up interpretting the response as the payload. In order to avoid such race, let's wait until the OS gets back the ownership before we prepare the shared memory with the payload for the next command. Reported-by: Jim Quinlan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 69bf85fea967..ccbe34da801a 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -265,6 +265,14 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); struct scmi_shared_mem __iomem *mem = cinfo->payload; + /* + * Ideally channel must be free by now unless OS timeout last + * request and platform continued to process the same, wait + * until it releases the shared memory, otherwise we may endup + * overwriting its response with new message payload or vice-versa + */ + spin_until_cond(ioread32(&mem->channel_status) & + SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE); /* Mark channel busy + clear error */ iowrite32(0x0, &mem->channel_status); iowrite32(t->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED, From 2747a967c85d1745c497599c632f1319a58aed5f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:42 +0100 Subject: [PATCH 043/123] firmware: arm_scmi: Reorder some functions to avoid forward declarations Re-shuffling few functions to keep definitions and their usages close. This is also needed to avoid too many unnecessary forward declarations while adding new features(delayed response and notifications). Keeping this separate to avoid mixing up of these trivial change that doesn't affect functionality into the ones that does. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 180 ++++++++++++++--------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ccbe34da801a..8bd350b1e108 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -197,44 +197,6 @@ static void scmi_fetch_response(struct scmi_xfer *xfer, memcpy_fromio(xfer->rx.buf, mem->msg_payload + 4, xfer->rx.len); } -/** - * scmi_rx_callback() - mailbox client callback for receive messages - * - * @cl: client pointer - * @m: mailbox message - * - * Processes one received message to appropriate transfer information and - * signals completion of the transfer. - * - * NOTE: This function will be invoked in IRQ context, hence should be - * as optimal as possible. - */ -static void scmi_rx_callback(struct mbox_client *cl, void *m) -{ - u16 xfer_id; - struct scmi_xfer *xfer; - struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); - struct device *dev = cinfo->dev; - struct scmi_info *info = handle_to_scmi_info(cinfo->handle); - struct scmi_xfers_info *minfo = &info->minfo; - struct scmi_shared_mem __iomem *mem = cinfo->payload; - - xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); - - /* Are we even expecting this? */ - if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { - dev_err(dev, "message for %d is not expected!\n", xfer_id); - return; - } - - xfer = &minfo->xfer_block[xfer_id]; - - scmi_dump_header_dbg(dev, &xfer->hdr); - - scmi_fetch_response(xfer, mem); - complete(&xfer->done); -} - /** * pack_scmi_header() - packs and returns 32-bit header * @@ -349,6 +311,44 @@ void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) spin_unlock_irqrestore(&minfo->xfer_lock, flags); } +/** + * scmi_rx_callback() - mailbox client callback for receive messages + * + * @cl: client pointer + * @m: mailbox message + * + * Processes one received message to appropriate transfer information and + * signals completion of the transfer. + * + * NOTE: This function will be invoked in IRQ context, hence should be + * as optimal as possible. + */ +static void scmi_rx_callback(struct mbox_client *cl, void *m) +{ + u16 xfer_id; + struct scmi_xfer *xfer; + struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); + struct device *dev = cinfo->dev; + struct scmi_info *info = handle_to_scmi_info(cinfo->handle); + struct scmi_xfers_info *minfo = &info->minfo; + struct scmi_shared_mem __iomem *mem = cinfo->payload; + + xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); + + /* Are we even expecting this? */ + if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { + dev_err(dev, "message for %d is not expected!\n", xfer_id); + return; + } + + xfer = &minfo->xfer_block[xfer_id]; + + scmi_dump_header_dbg(dev, &xfer->hdr); + + scmi_fetch_response(xfer, mem); + complete(&xfer->done); +} + static bool scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { @@ -599,20 +599,6 @@ int scmi_handle_put(const struct scmi_handle *handle) return 0; } -static const struct scmi_desc scmi_generic_desc = { - .max_rx_timeout_ms = 30, /* We may increase this if required */ - .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ - .max_msg_size = 128, -}; - -/* Each compatible listed below must have descriptor associated with it */ -static const struct of_device_id scmi_of_match[] = { - { .compatible = "arm,scmi", .data = &scmi_generic_desc }, - { /* Sentinel */ }, -}; - -MODULE_DEVICE_TABLE(of, scmi_of_match); - static int scmi_xfer_info_init(struct scmi_info *sinfo) { int i; @@ -659,44 +645,6 @@ static int scmi_mailbox_check(struct device_node *np) return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } -static int scmi_mbox_free_channel(int id, void *p, void *data) -{ - struct scmi_chan_info *cinfo = p; - struct idr *idr = data; - - if (!IS_ERR_OR_NULL(cinfo->chan)) { - mbox_free_channel(cinfo->chan); - cinfo->chan = NULL; - } - - idr_remove(idr, id); - - return 0; -} - -static int scmi_remove(struct platform_device *pdev) -{ - int ret = 0; - struct scmi_info *info = platform_get_drvdata(pdev); - struct idr *idr = &info->tx_idr; - - mutex_lock(&scmi_list_mutex); - if (info->users) - ret = -EBUSY; - else - list_del(&info->node); - mutex_unlock(&scmi_list_mutex); - - if (ret) - return ret; - - /* Safe to free channels since no more users */ - ret = idr_for_each(idr, scmi_mbox_free_channel, idr); - idr_destroy(&info->tx_idr); - - return ret; -} - static inline int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id) { @@ -856,6 +804,58 @@ static int scmi_probe(struct platform_device *pdev) return 0; } +static int scmi_mbox_free_channel(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct idr *idr = data; + + if (!IS_ERR_OR_NULL(cinfo->chan)) { + mbox_free_channel(cinfo->chan); + cinfo->chan = NULL; + } + + idr_remove(idr, id); + + return 0; +} + +static int scmi_remove(struct platform_device *pdev) +{ + int ret = 0; + struct scmi_info *info = platform_get_drvdata(pdev); + struct idr *idr = &info->tx_idr; + + mutex_lock(&scmi_list_mutex); + if (info->users) + ret = -EBUSY; + else + list_del(&info->node); + mutex_unlock(&scmi_list_mutex); + + if (ret) + return ret; + + /* Safe to free channels since no more users */ + ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + idr_destroy(&info->tx_idr); + + return ret; +} + +static const struct scmi_desc scmi_generic_desc = { + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ + .max_msg_size = 128, +}; + +/* Each compatible listed below must have descriptor associated with it */ +static const struct of_device_id scmi_of_match[] = { + { .compatible = "arm,scmi", .data = &scmi_generic_desc }, + { /* Sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, scmi_of_match); + static struct platform_driver scmi_driver = { .driver = { .name = "arm-scmi", From 3748daf7fb6b5459c7a62d4976b9698b82ee9b48 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:44 +0100 Subject: [PATCH 044/123] firmware: arm_scmi: Segregate tx channel handling and prepare to add rx The transmit(Tx) channels are specified as the first entry and the receive(Rx) channels are the second entry as per the device tree bindings. Since we currently just support Tx, index 0 is hardcoded at all required callsites. In order to prepare for adding Rx support, let's remove those hardcoded index and add boolean parameter to identify Tx/Rx channels when setting them up. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 41 +++++++++++++++++------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 8bd350b1e108..08787aed5519 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -112,7 +112,7 @@ struct scmi_chan_info { * @version: SCMI revision information containing protocol version, * implementation version and (sub-)vendor identification. * @minfo: Message info - * @tx_idr: IDR object to map protocol id to channel info pointer + * @tx_idr: IDR object to map protocol id to Tx channel info pointer * @protocols_imp: List of protocols implemented, currently maximum of * MAX_PROTOCOLS_IMP elements allocated by the base protocol * @node: List head @@ -640,22 +640,27 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) return 0; } -static int scmi_mailbox_check(struct device_node *np) +static int scmi_mailbox_check(struct device_node *np, int idx) { - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", + idx, NULL); } -static inline int -scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id) +static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, + int prot_id, bool tx) { - int ret; + int ret, idx; struct resource res; resource_size_t size; struct device_node *shmem, *np = dev->of_node; struct scmi_chan_info *cinfo; struct mbox_client *cl; + const char *desc = tx ? "Tx" : "Rx"; - if (scmi_mailbox_check(np)) { + /* Transmit channel is first entry i.e. index 0 */ + idx = tx ? 0 : 1; + + if (scmi_mailbox_check(np, idx)) { cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE); goto idr_alloc; } @@ -669,31 +674,31 @@ scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id) cl = &cinfo->cl; cl->dev = dev; cl->rx_callback = scmi_rx_callback; - cl->tx_prepare = scmi_tx_prepare; + cl->tx_prepare = tx ? scmi_tx_prepare : NULL; cl->tx_block = false; - cl->knows_txdone = true; + cl->knows_txdone = tx; - shmem = of_parse_phandle(np, "shmem", 0); + shmem = of_parse_phandle(np, "shmem", idx); ret = of_address_to_resource(shmem, 0, &res); of_node_put(shmem); if (ret) { - dev_err(dev, "failed to get SCMI Tx payload mem resource\n"); + dev_err(dev, "failed to get SCMI %s payload memory\n", desc); return ret; } size = resource_size(&res); cinfo->payload = devm_ioremap(info->dev, res.start, size); if (!cinfo->payload) { - dev_err(dev, "failed to ioremap SCMI Tx payload\n"); + dev_err(dev, "failed to ioremap SCMI %s payload\n", desc); return -EADDRNOTAVAIL; } - /* Transmit channel is first entry i.e. index 0 */ - cinfo->chan = mbox_request_channel(cl, 0); + cinfo->chan = mbox_request_channel(cl, idx); if (IS_ERR(cinfo->chan)) { ret = PTR_ERR(cinfo->chan); if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to request SCMI Tx mailbox\n"); + dev_err(dev, "failed to request SCMI %s mailbox\n", + desc); return ret; } @@ -721,7 +726,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, return; } - if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) { + if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id, true)) { dev_err(&sdev->dev, "failed to setup transport\n"); scmi_device_destroy(sdev); return; @@ -741,7 +746,7 @@ static int scmi_probe(struct platform_device *pdev) struct device_node *child, *np = dev->of_node; /* Only mailbox method supported, check for the presence of one */ - if (scmi_mailbox_check(np)) { + if (scmi_mailbox_check(np, 0)) { dev_err(dev, "no mailbox found in %pOF\n", np); return -EINVAL; } @@ -769,7 +774,7 @@ static int scmi_probe(struct platform_device *pdev) handle->dev = info->dev; handle->version = &info->version; - ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE); + ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE, true); if (ret) return ret; From 46cc7c286ce33f729055f28c114d84e6340c08dd Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:46 +0100 Subject: [PATCH 045/123] firmware: arm_scmi: Add receive channel support for notifications With scmi_mbox_chan_setup enabled to identify and setup both Tx and Rx, let's consolidate setting up of both the channels under the function scmi_mbox_txrx_setup. Since some platforms may opt not to support notifications or delayed response, they may not need support for Rx. Hence Rx is optional and failure of setting one up is not considered fatal. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 08787aed5519..2d5aaff5e3ec 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -113,6 +113,7 @@ struct scmi_chan_info { * implementation version and (sub-)vendor identification. * @minfo: Message info * @tx_idr: IDR object to map protocol id to Tx channel info pointer + * @rx_idr: IDR object to map protocol id to Rx channel info pointer * @protocols_imp: List of protocols implemented, currently maximum of * MAX_PROTOCOLS_IMP elements allocated by the base protocol * @node: List head @@ -125,6 +126,7 @@ struct scmi_info { struct scmi_handle handle; struct scmi_xfers_info minfo; struct idr tx_idr; + struct idr rx_idr; u8 *protocols_imp; struct list_head node; int users; @@ -655,13 +657,17 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, struct device_node *shmem, *np = dev->of_node; struct scmi_chan_info *cinfo; struct mbox_client *cl; + struct idr *idr; const char *desc = tx ? "Tx" : "Rx"; /* Transmit channel is first entry i.e. index 0 */ idx = tx ? 0 : 1; + idr = tx ? &info->tx_idr : &info->rx_idr; if (scmi_mailbox_check(np, idx)) { - cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE); + cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); + if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ + return -EINVAL; goto idr_alloc; } @@ -703,7 +709,7 @@ static int scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, } idr_alloc: - ret = idr_alloc(&info->tx_idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); + ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL); if (ret != prot_id) { dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret); return ret; @@ -713,6 +719,17 @@ idr_alloc: return 0; } +static inline int +scmi_mbox_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id) +{ + int ret = scmi_mbox_chan_setup(info, dev, prot_id, true); + + if (!ret) /* Rx is optional, hence no error check */ + scmi_mbox_chan_setup(info, dev, prot_id, false); + + return ret; +} + static inline void scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, int prot_id) @@ -726,7 +743,7 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info, return; } - if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id, true)) { + if (scmi_mbox_txrx_setup(info, &sdev->dev, prot_id)) { dev_err(&sdev->dev, "failed to setup transport\n"); scmi_device_destroy(sdev); return; @@ -769,12 +786,13 @@ static int scmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); idr_init(&info->tx_idr); + idr_init(&info->rx_idr); handle = &info->handle; handle->dev = info->dev; handle->version = &info->version; - ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE, true); + ret = scmi_mbox_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); if (ret) return ret; @@ -844,6 +862,10 @@ static int scmi_remove(struct platform_device *pdev) ret = idr_for_each(idr, scmi_mbox_free_channel, idr); idr_destroy(&info->tx_idr); + idr = &info->rx_idr; + ret = idr_for_each(idr, scmi_mbox_free_channel, idr); + idr_destroy(&info->rx_idr); + return ret; } From 38c927fbebb3d69c561ae77600f64bb1c308d0c2 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:48 +0100 Subject: [PATCH 046/123] firmware: arm_scmi: Separate out tx buffer handling and prepare to add rx Currently we pre-allocate transmit buffers only and use the first free slot in that pre-allocated buffer for transmitting any new message that are generally originated from OS to the platform firmware. Notifications or the delayed responses on the other hand are originated from the platform firmware and consumes by the OS. It's better to have separate and dedicated pre-allocated buffers to handle the notifications. We can still use the transmit buffers for the delayed responses. In addition, let's prepare existing scmi_xfer_{get,put} for acquiring and releasing a slot to identify the right(tx/rx) buffers. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 40 ++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 2d5aaff5e3ec..989e59f3d141 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -111,7 +111,7 @@ struct scmi_chan_info { * @handle: Instance of SCMI handle to send to clients * @version: SCMI revision information containing protocol version, * implementation version and (sub-)vendor identification. - * @minfo: Message info + * @tx_minfo: Universal Transmit Message management info * @tx_idr: IDR object to map protocol id to Tx channel info pointer * @rx_idr: IDR object to map protocol id to Rx channel info pointer * @protocols_imp: List of protocols implemented, currently maximum of @@ -124,7 +124,7 @@ struct scmi_info { const struct scmi_desc *desc; struct scmi_revision_info version; struct scmi_handle handle; - struct scmi_xfers_info minfo; + struct scmi_xfers_info tx_minfo; struct idr tx_idr; struct idr rx_idr; u8 *protocols_imp; @@ -251,6 +251,7 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) * scmi_xfer_get() - Allocate one message * * @handle: Pointer to SCMI entity handle + * @minfo: Pointer to Tx/Rx Message management info based on channel type * * Helper function which is used by various message functions that are * exposed to clients of this driver for allocating a message traffic event. @@ -261,13 +262,13 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) * * Return: 0 if all went fine, else corresponding error. */ -static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle) +static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle, + struct scmi_xfers_info *minfo) { u16 xfer_id; struct scmi_xfer *xfer; unsigned long flags, bit_pos; struct scmi_info *info = handle_to_scmi_info(handle); - struct scmi_xfers_info *minfo = &info->minfo; /* Keep the locked section as small as possible */ spin_lock_irqsave(&minfo->xfer_lock, flags); @@ -290,18 +291,17 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle) } /** - * scmi_xfer_put() - Release a message + * __scmi_xfer_put() - Release a message * - * @handle: Pointer to SCMI entity handle + * @minfo: Pointer to Tx/Rx Message management info based on channel type * @xfer: message that was reserved by scmi_xfer_get * * This holds a spinlock to maintain integrity of internal data structures. */ -void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) +static void +__scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) { unsigned long flags; - struct scmi_info *info = handle_to_scmi_info(handle); - struct scmi_xfers_info *minfo = &info->minfo; /* * Keep the locked section as small as possible @@ -332,7 +332,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); struct device *dev = cinfo->dev; struct scmi_info *info = handle_to_scmi_info(cinfo->handle); - struct scmi_xfers_info *minfo = &info->minfo; + struct scmi_xfers_info *minfo = &info->tx_minfo; struct scmi_shared_mem __iomem *mem = cinfo->payload; xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); @@ -351,6 +351,19 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) complete(&xfer->done); } +/** + * scmi_xfer_put() - Release a transmit message + * + * @handle: Pointer to SCMI entity handle + * @xfer: message that was reserved by scmi_xfer_get + */ +void scmi_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer) +{ + struct scmi_info *info = handle_to_scmi_info(handle); + + __scmi_xfer_put(&info->tx_minfo, xfer); +} + static bool scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { @@ -440,7 +453,7 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) } /** - * scmi_xfer_get_init() - Allocate and initialise one message + * scmi_xfer_get_init() - Allocate and initialise one message for transmit * * @handle: Pointer to SCMI entity handle * @msg_id: Message identifier @@ -461,6 +474,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id, int ret; struct scmi_xfer *xfer; struct scmi_info *info = handle_to_scmi_info(handle); + struct scmi_xfers_info *minfo = &info->tx_minfo; struct device *dev = info->dev; /* Ensure we have sane transfer sizes */ @@ -468,7 +482,7 @@ int scmi_xfer_get_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id, tx_size > info->desc->max_msg_size) return -ERANGE; - xfer = scmi_xfer_get(handle); + xfer = scmi_xfer_get(handle, minfo); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); dev_err(dev, "failed to get free message slot(%d)\n", ret); @@ -607,7 +621,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) struct scmi_xfer *xfer; struct device *dev = sinfo->dev; const struct scmi_desc *desc = sinfo->desc; - struct scmi_xfers_info *info = &sinfo->minfo; + struct scmi_xfers_info *info = &sinfo->tx_minfo; /* Pre-allocated messages, no more than what hdr.seq can support */ if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { From 22d1f76109f74b716a5f4d46bade61f544f40f81 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:52 +0100 Subject: [PATCH 047/123] firmware: arm_scmi: Add mechanism to unpack message headers In order to identify the message type when a response arrives, we need a mechanism to unpack the message header similar to packing. Let's add one. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 989e59f3d141..835a1d9e2b9a 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -30,8 +30,14 @@ #include "common.h" #define MSG_ID_MASK GENMASK(7, 0) +#define MSG_XTRACT_ID(hdr) FIELD_GET(MSG_ID_MASK, (hdr)) #define MSG_TYPE_MASK GENMASK(9, 8) +#define MSG_XTRACT_TYPE(hdr) FIELD_GET(MSG_TYPE_MASK, (hdr)) +#define MSG_TYPE_COMMAND 0 +#define MSG_TYPE_DELAYED_RESP 2 +#define MSG_TYPE_NOTIFICATION 3 #define MSG_PROTOCOL_ID_MASK GENMASK(17, 10) +#define MSG_XTRACT_PROT_ID(hdr) FIELD_GET(MSG_PROTOCOL_ID_MASK, (hdr)) #define MSG_TOKEN_ID_MASK GENMASK(27, 18) #define MSG_XTRACT_TOKEN(hdr) FIELD_GET(MSG_TOKEN_ID_MASK, (hdr)) #define MSG_TOKEN_MAX (MSG_XTRACT_TOKEN(MSG_TOKEN_ID_MASK) + 1) @@ -214,6 +220,18 @@ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) FIELD_PREP(MSG_PROTOCOL_ID_MASK, hdr->protocol_id); } +/** + * unpack_scmi_header() - unpacks and records message and protocol id + * + * @msg_hdr: 32-bit packed message header sent from the platform + * @hdr: pointer to header to fetch message and protocol id. + */ +static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr) +{ + hdr->id = MSG_XTRACT_ID(msg_hdr); + hdr->protocol_id = MSG_XTRACT_PROT_ID(msg_hdr); +} + /** * scmi_tx_prepare() - mailbox client callback to prepare for the transfer * From 58ecdf03dbb9ce8f130bc1ac63fcd8f4d72f9c06 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:54 +0100 Subject: [PATCH 048/123] firmware: arm_scmi: Add support for asynchronous commands and delayed response Messages that are sent to platform, also known as commands and can be: 1. Synchronous commands that block the channel until the requested work has been completed. The platform responds to these commands over the same channel and hence can't be used to send another command until the previous command has completed. 2. Asynchronous commands on the other hand, the platform schedules the requested work to complete later in time and returns almost immediately freeing the channel for new commands. The response indicates the success or failure in the ability to schedule the requested work. When the work has completed, the platform sends an additional delayed response message. Using the same transmit buffer used for sending the asynchronous command even for the delayed response corresponding to it simplifies handling of the delayed response. It's the caller of asynchronous command that is responsible for allocating the completion flag that scmi driver can complete to indicate the arrival of delayed response. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/common.h | 6 ++++- drivers/firmware/arm_scmi/driver.c | 43 ++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index a9eee62c7142..43884e4ceac5 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -84,17 +84,21 @@ struct scmi_msg { * @rx: Receive message, the buffer should be pre-allocated to store * message. If request-ACK protocol is used, we can reuse the same * buffer for the rx path as we use for the tx path. - * @done: completion event + * @done: command message transmit completion event + * @async: pointer to delayed response message received event completion */ struct scmi_xfer { struct scmi_msg_hdr hdr; struct scmi_msg tx; struct scmi_msg rx; struct completion done; + struct completion *async_done; }; void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer); int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer); +int scmi_do_xfer_with_response(const struct scmi_handle *h, + struct scmi_xfer *xfer); int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id, size_t tx_size, size_t rx_size, struct scmi_xfer **p); int scmi_handle_put(const struct scmi_handle *handle); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 835a1d9e2b9a..3eb0382491ce 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -345,6 +345,8 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer) */ static void scmi_rx_callback(struct mbox_client *cl, void *m) { + u8 msg_type; + u32 msg_hdr; u16 xfer_id; struct scmi_xfer *xfer; struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl); @@ -353,7 +355,12 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) struct scmi_xfers_info *minfo = &info->tx_minfo; struct scmi_shared_mem __iomem *mem = cinfo->payload; - xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); + msg_hdr = ioread32(&mem->msg_header); + msg_type = MSG_XTRACT_TYPE(msg_hdr); + xfer_id = MSG_XTRACT_TOKEN(msg_hdr); + + if (msg_type == MSG_TYPE_NOTIFICATION) + return; /* Notifications not yet supported */ /* Are we even expecting this? */ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { @@ -366,7 +373,11 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) scmi_dump_header_dbg(dev, &xfer->hdr); scmi_fetch_response(xfer, mem); - complete(&xfer->done); + + if (msg_type == MSG_TYPE_DELAYED_RESP) + complete(xfer->async_done); + else + complete(&xfer->done); } /** @@ -470,6 +481,34 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) return ret; } +#define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC) + +/** + * scmi_do_xfer_with_response() - Do one transfer and wait until the delayed + * response is received + * + * @handle: Pointer to SCMI entity handle + * @xfer: Transfer to initiate and wait for response + * + * Return: -ETIMEDOUT in case of no delayed response, if transmit error, + * return corresponding error, else if all goes well, return 0. + */ +int scmi_do_xfer_with_response(const struct scmi_handle *handle, + struct scmi_xfer *xfer) +{ + int ret, timeout = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT); + DECLARE_COMPLETION_ONSTACK(async_response); + + xfer->async_done = &async_response; + + ret = scmi_do_xfer(handle, xfer); + if (!ret && !wait_for_completion_timeout(xfer->async_done, timeout)) + ret = -ETIMEDOUT; + + xfer->async_done = NULL; + return ret; +} + /** * scmi_xfer_get_init() - Allocate and initialise one message for transmit * From 6a55331c87d86a7406d8126ae75bdd07244a91b1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:40:57 +0100 Subject: [PATCH 049/123] firmware: arm_scmi: Drop async flag in sensor_ops->reading_get SENSOR_DESCRIPTION_GET provides attributes to indicate if the sensor supports asynchronous read. Ideally we should be able to read that flag and use asynchronous reads for any sensors with that attribute set. In order to add that support, let's drop the async flag passed to sensor_ops->reading_get and dynamically switch between sync and async flags based on the attributes as provided by the firmware. Cc: linux-hwmon@vger.kernel.org Acked-by: Guenter Roeck Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 4 ++-- drivers/hwmon/scmi-hwmon.c | 2 +- include/linux/scmi_protocol.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 17dbabd8a94a..1b5757c77a35 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -211,7 +211,7 @@ scmi_sensor_trip_point_config(const struct scmi_handle *handle, u32 sensor_id, } static int scmi_sensor_reading_get(const struct scmi_handle *handle, - u32 sensor_id, bool async, u64 *value) + u32 sensor_id, u64 *value) { int ret; struct scmi_xfer *t; @@ -225,7 +225,7 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, sensor = t->tx.buf; sensor->id = cpu_to_le32(sensor_id); - sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0); + sensor->flags = cpu_to_le32(0); ret = scmi_do_xfer(handle, t); if (!ret) { diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 0c93fc5ca762..8a7732c0bef3 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -72,7 +72,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, const struct scmi_handle *h = scmi_sensors->handle; sensor = *(scmi_sensors->info[type] + channel); - ret = h->sensor_ops->reading_get(h, sensor->id, false, &value); + ret = h->sensor_ops->reading_get(h, sensor->id, &value); if (ret) return ret; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 1383d47e6435..2ace5af210ad 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -182,7 +182,7 @@ struct scmi_sensor_ops { int (*trip_point_config)(const struct scmi_handle *handle, u32 sensor_id, u8 trip_id, u64 trip_value); int (*reading_get)(const struct scmi_handle *handle, u32 sensor_id, - bool async, u64 *value); + u64 *value); }; /** From d09aac0eb17c6ce2b66095e1e324f60ec9dd8988 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:01 +0100 Subject: [PATCH 050/123] firmware: arm_scmi: Add asynchronous sensor read if it supports SENSOR_DESCRIPTION_GET provides attributes to indicate if the sensor supports asynchronous read. We can read that flag and use asynchronous reads for any sensors with that attribute set. Let's use the new scmi_do_xfer_with_response to support asynchronous sensor reads. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 30 +++++++++++++++++++++-------- include/linux/scmi_protocol.h | 2 ++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 1b5757c77a35..7570308a16a0 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -136,9 +136,10 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, } for (cnt = 0; cnt < num_returned; cnt++) { - u32 attrh; + u32 attrh, attrl; struct scmi_sensor_info *s; + attrl = le32_to_cpu(buf->desc[cnt].attributes_low); attrh = le32_to_cpu(buf->desc[cnt].attributes_high); s = &si->sensors[desc_index + cnt]; s->id = le32_to_cpu(buf->desc[cnt].id); @@ -147,6 +148,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, /* Sign extend to a full s8 */ if (s->scale & SENSOR_SCALE_SIGN) s->scale |= SENSOR_SCALE_EXTEND; + s->async = SUPPORTS_ASYNC_READ(attrl); + s->num_trip_points = NUM_TRIP_POINTS(attrl); strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); } @@ -214,8 +217,11 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, u32 sensor_id, u64 *value) { int ret; + __le32 *pval; struct scmi_xfer *t; struct scmi_msg_sensor_reading_get *sensor; + struct sensors_info *si = handle->sensor_priv; + struct scmi_sensor_info *s = si->sensors + sensor_id; ret = scmi_xfer_get_init(handle, SENSOR_READING_GET, SCMI_PROTOCOL_SENSOR, sizeof(*sensor), @@ -223,16 +229,24 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, if (ret) return ret; + pval = t->rx.buf; sensor = t->tx.buf; sensor->id = cpu_to_le32(sensor_id); - sensor->flags = cpu_to_le32(0); - ret = scmi_do_xfer(handle, t); - if (!ret) { - __le32 *pval = t->rx.buf; - - *value = le32_to_cpu(*pval); - *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; + if (s->async) { + sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC); + ret = scmi_do_xfer_with_response(handle, t); + if (!ret) { + *value = le32_to_cpu(*(pval + 1)); + *value |= (u64)le32_to_cpu(*(pval + 2)) << 32; + } + } else { + sensor->flags = cpu_to_le32(0); + ret = scmi_do_xfer(handle, t); + if (!ret) { + *value = le32_to_cpu(*pval); + *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; + } } scmi_xfer_put(handle, t); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 2ace5af210ad..ae7381413f1f 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -145,6 +145,8 @@ struct scmi_sensor_info { u32 id; u8 type; s8 scale; + u8 num_trip_points; + bool async; char name[SCMI_MAX_STR_SIZE]; }; From d0aba11614552d43a61c3acdf36876b00060286e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:42:22 +0100 Subject: [PATCH 051/123] firmware: arm_scmi: Drop config flag in clk_ops->rate_set CLOCK_PROTOCOL_ATTRIBUTES provides attributes to indicate the maximum number of pending asynchronous clock rate changes supported by the platform. If it's non-zero, then we should be able to use asynchronous clock rate set for any clocks until the maximum limit is reached. In order to add that support, let's drop the config flag passed to clk_ops->rate_set and handle the asynchronous requests dynamically. Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Acked-by: Stephen Boyd Signed-off-by: Sudeep Holla --- drivers/clk/clk-scmi.c | 2 +- drivers/firmware/arm_scmi/clock.c | 4 ++-- include/linux/scmi_protocol.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index a2287c770d5c..886f7c5df51a 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -69,7 +69,7 @@ static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate, { struct scmi_clk *clk = to_scmi_clk(hw); - return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate); + return clk->handle->clk_ops->rate_set(clk->handle, clk->id, rate); } static int scmi_clk_enable(struct clk_hw *hw) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 0a194af92438..dd215bd11a58 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -218,7 +218,7 @@ scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value) } static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id, - u32 config, u64 rate) + u64 rate) { int ret; struct scmi_xfer *t; @@ -230,7 +230,7 @@ static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id, return ret; cfg = t->tx.buf; - cfg->flags = cpu_to_le32(config); + cfg->flags = cpu_to_le32(0); cfg->id = cpu_to_le32(clk_id); cfg->value_low = cpu_to_le32(rate & 0xffffffff); cfg->value_high = cpu_to_le32(rate >> 32); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index ae7381413f1f..f0f2b53a1dac 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -71,7 +71,7 @@ struct scmi_clk_ops { int (*rate_get)(const struct scmi_handle *handle, u32 clk_id, u64 *rate); int (*rate_set)(const struct scmi_handle *handle, u32 clk_id, - u32 config, u64 rate); + u64 rate); int (*enable)(const struct scmi_handle *handle, u32 clk_id); int (*disable)(const struct scmi_handle *handle, u32 clk_id); }; From 2bc06ffa0635e99cd4d88d6f6fe5bb4e4b9e50f1 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 14:42:16 +0100 Subject: [PATCH 052/123] firmware: arm_scmi: Use asynchronous CLOCK_RATE_SET when possible CLOCK_PROTOCOL_ATTRIBUTES provides attributes to indicate the maximum number of pending asynchronous clock rate changes supported by the platform. If it's non-zero, then we should be able to use asynchronous clock rate set for any clocks until the maximum limit is reached. Tracking the current count of pending asynchronous clock set rate requests, we can decide if the incoming/new request for clock set rate can be handled asynchronously or not until the maximum limit is reached. Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index dd215bd11a58..4a32ae1822a3 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -56,7 +56,7 @@ struct scmi_msg_resp_clock_describe_rates { struct scmi_clock_set_rate { __le32 flags; #define CLOCK_SET_ASYNC BIT(0) -#define CLOCK_SET_DELAYED BIT(1) +#define CLOCK_SET_IGNORE_RESP BIT(1) #define CLOCK_SET_ROUND_UP BIT(2) #define CLOCK_SET_ROUND_AUTO BIT(3) __le32 id; @@ -67,6 +67,7 @@ struct scmi_clock_set_rate { struct clock_info { int num_clocks; int max_async_req; + atomic_t cur_async_req; struct scmi_clock_info *clk; }; @@ -221,21 +222,33 @@ static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id, u64 rate) { int ret; + u32 flags = 0; struct scmi_xfer *t; struct scmi_clock_set_rate *cfg; + struct clock_info *ci = handle->clk_priv; ret = scmi_xfer_get_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK, sizeof(*cfg), 0, &t); if (ret) return ret; + if (ci->max_async_req && + atomic_inc_return(&ci->cur_async_req) < ci->max_async_req) + flags |= CLOCK_SET_ASYNC; + cfg = t->tx.buf; - cfg->flags = cpu_to_le32(0); + cfg->flags = cpu_to_le32(flags); cfg->id = cpu_to_le32(clk_id); cfg->value_low = cpu_to_le32(rate & 0xffffffff); cfg->value_high = cpu_to_le32(rate >> 32); - ret = scmi_do_xfer(handle, t); + if (flags & CLOCK_SET_ASYNC) + ret = scmi_do_xfer_with_response(handle, t); + else + ret = scmi_do_xfer(handle, t); + + if (ci->max_async_req) + atomic_dec(&ci->cur_async_req); scmi_xfer_put(handle, t); return ret; From aa90ac45bc88e6a8efb199e45dab5d5a6b01c3d2 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 7 Aug 2019 13:46:27 +0100 Subject: [PATCH 053/123] firmware: arm_scmi: Use {get,put}_unaligned_le{32,64} accessors Instead of type-casting the {tx,rx}.buf all over the place while accessing them to read/write __le{32,64} from/to the firmware, let's use the existing {get,put}_unaligned_le{32,64} accessors to hide all the type cast ugliness. Suggested-by: Philipp Zabel Reviewed-by: Philipp Zabel Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 2 +- drivers/firmware/arm_scmi/clock.c | 12 ++++-------- drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/perf.c | 8 ++++---- drivers/firmware/arm_scmi/power.c | 6 +++--- drivers/firmware/arm_scmi/sensors.c | 17 ++++++----------- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 204390297f4b..f804e8af6521 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -204,7 +204,7 @@ static int scmi_base_discover_agent_get(const struct scmi_handle *handle, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(id); + put_unaligned_le32(id, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 4a32ae1822a3..32526a793f3a 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -107,7 +107,7 @@ static int scmi_clock_attributes_get(const struct scmi_handle *handle, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(clk_id); + put_unaligned_le32(clk_id, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -204,15 +204,11 @@ scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value) if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(clk_id); + put_unaligned_le32(clk_id, t->tx.buf); ret = scmi_do_xfer(handle, t); - if (!ret) { - __le32 *pval = t->rx.buf; - - *value = le32_to_cpu(*pval); - *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; - } + if (!ret) + *value = get_unaligned_le64(t->rx.buf); scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 43884e4ceac5..5237c2ff79fe 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -15,6 +15,8 @@ #include #include +#include + #define PROTOCOL_REV_MINOR_MASK GENMASK(15, 0) #define PROTOCOL_REV_MAJOR_MASK GENMASK(31, 16) #define PROTOCOL_REV_MAJOR(x) (u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 3c8ae7cc35de..95b87cd44f94 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -151,7 +151,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -284,7 +284,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) { @@ -333,11 +333,11 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, return ret; t->hdr.poll_completion = poll; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) - *level = le32_to_cpu(*(__le32 *)t->rx.buf); + *level = get_unaligned_le32(t->rx.buf); scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index 62f3401a1f01..5abef7079c0a 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -96,7 +96,7 @@ scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain, if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); attr = t->rx.buf; ret = scmi_do_xfer(handle, t); @@ -147,11 +147,11 @@ scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state) if (ret) return ret; - *(__le32 *)t->tx.buf = cpu_to_le32(domain); + put_unaligned_le32(domain, t->tx.buf); ret = scmi_do_xfer(handle, t); if (!ret) - *state = le32_to_cpu(*(__le32 *)t->rx.buf); + *state = get_unaligned_le32(t->rx.buf); scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 7570308a16a0..a400ea805fc2 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -120,7 +120,7 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, do { /* Set the number of sensors to be skipped/already read */ - *(__le32 *)t->tx.buf = cpu_to_le32(desc_index); + put_unaligned_le32(desc_index, t->tx.buf); ret = scmi_do_xfer(handle, t); if (ret) @@ -217,7 +217,6 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, u32 sensor_id, u64 *value) { int ret; - __le32 *pval; struct scmi_xfer *t; struct scmi_msg_sensor_reading_get *sensor; struct sensors_info *si = handle->sensor_priv; @@ -229,24 +228,20 @@ static int scmi_sensor_reading_get(const struct scmi_handle *handle, if (ret) return ret; - pval = t->rx.buf; sensor = t->tx.buf; sensor->id = cpu_to_le32(sensor_id); if (s->async) { sensor->flags = cpu_to_le32(SENSOR_READ_ASYNC); ret = scmi_do_xfer_with_response(handle, t); - if (!ret) { - *value = le32_to_cpu(*(pval + 1)); - *value |= (u64)le32_to_cpu(*(pval + 2)) << 32; - } + if (!ret) + *value = get_unaligned_le64((void *) + ((__le32 *)t->rx.buf + 1)); } else { sensor->flags = cpu_to_le32(0); ret = scmi_do_xfer(handle, t); - if (!ret) { - *value = le32_to_cpu(*pval); - *value |= (u64)le32_to_cpu(*(pval + 1)) << 32; - } + if (!ret) + *value = get_unaligned_le64(t->rx.buf); } scmi_xfer_put(handle, t); From ac8aaf348cf54a07aff8e709329ef82ecfa230cc Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:12 +0100 Subject: [PATCH 054/123] firmware: arm_scmi: Add discovery of SCMI v2.0 performance fastchannels SCMI v2.0 adds support for "FastChannel", a lightweight unidirectional channel that is dedicated to a single SCMI message type for controlling a specific platform resource. They do not use a message header as they are specialized for a single message. Only PERFORMANCE_LIMITS_{SET,GET} and PERFORMANCE_LEVEL_{SET,GET} commands are supported over fastchannels. As they are optional, they need to be discovered by PERFORMANCE_DESCRIBE_FASTCHANNEL command. Further {LIMIT,LEVEL}_SET commands can have optional doorbell support. Add support for discovery of these fastchannels. Cc: Ionela Voinescu Cc: Chris Redpath Cc: Quentin Perret Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 153 ++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 95b87cd44f94..b0951b175578 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -5,7 +5,9 @@ * Copyright (C) 2018 ARM Ltd. */ +#include #include +#include #include #include #include @@ -21,6 +23,7 @@ enum scmi_performance_protocol_cmd { PERF_LEVEL_GET = 0x8, PERF_NOTIFY_LIMITS = 0x9, PERF_NOTIFY_LEVEL = 0xa, + PERF_DESCRIBE_FASTCHANNEL = 0xb, }; struct scmi_opp { @@ -44,6 +47,7 @@ struct scmi_msg_resp_perf_domain_attributes { #define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30)) #define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29)) #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28)) +#define SUPPORTS_PERF_FASTCHANNELS(x) ((x) & BIT(27)) __le32 rate_limit_us; __le32 sustained_freq_khz; __le32 sustained_perf_level; @@ -87,17 +91,56 @@ struct scmi_msg_resp_perf_describe_levels { } opp[0]; }; +struct scmi_perf_get_fc_info { + __le32 domain; + __le32 message_id; +}; + +struct scmi_msg_resp_perf_desc_fc { + __le32 attr; +#define SUPPORTS_DOORBELL(x) ((x) & BIT(0)) +#define DOORBELL_REG_WIDTH(x) FIELD_GET(GENMASK(2, 1), (x)) + __le32 rate_limit; + __le32 chan_addr_low; + __le32 chan_addr_high; + __le32 chan_size; + __le32 db_addr_low; + __le32 db_addr_high; + __le32 db_set_lmask; + __le32 db_set_hmask; + __le32 db_preserve_lmask; + __le32 db_preserve_hmask; +}; + +struct scmi_fc_db_info { + int width; + u64 set; + u64 mask; + void __iomem *addr; +}; + +struct scmi_fc_info { + void __iomem *level_set_addr; + void __iomem *limit_set_addr; + void __iomem *level_get_addr; + void __iomem *limit_get_addr; + struct scmi_fc_db_info *level_set_db; + struct scmi_fc_db_info *limit_set_db; +}; + struct perf_dom_info { bool set_limits; bool set_perf; bool perf_limit_notify; bool perf_level_notify; + bool perf_fastchannels; u32 opp_count; u32 sustained_freq_khz; u32 sustained_perf_level; u32 mult_factor; char name[SCMI_MAX_STR_SIZE]; struct scmi_opp opp[MAX_OPPS]; + struct scmi_fc_info *fc_info; }; struct scmi_perf_info { @@ -162,6 +205,7 @@ scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain, dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags); dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags); dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); + dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); dom_info->sustained_freq_khz = le32_to_cpu(attr->sustained_freq_khz); dom_info->sustained_perf_level = @@ -250,7 +294,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, } static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, - u32 max_perf, u32 min_perf) + u32 max_perf, u32 min_perf) { int ret; struct scmi_xfer *t; @@ -273,7 +317,7 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, } static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, - u32 *max_perf, u32 *min_perf) + u32 *max_perf, u32 *min_perf) { int ret; struct scmi_xfer *t; @@ -299,7 +343,7 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, } static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, - u32 level, bool poll) + u32 level, bool poll) { int ret; struct scmi_xfer *t; @@ -322,7 +366,7 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, } static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, - u32 *level, bool poll) + u32 *level, bool poll) { int ret; struct scmi_xfer *t; @@ -343,6 +387,104 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, return ret; } +static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size) +{ + if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4) + return true; + if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8) + return true; + return false; +} + +static void +scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain, + u32 message_id, void __iomem **p_addr, + struct scmi_fc_db_info **p_db) +{ + int ret; + u32 flags; + u64 phys_addr; + u8 size; + void __iomem *addr; + struct scmi_xfer *t; + struct scmi_fc_db_info *db; + struct scmi_perf_get_fc_info *info; + struct scmi_msg_resp_perf_desc_fc *resp; + + if (!p_addr) + return; + + ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL, + SCMI_PROTOCOL_PERF, + sizeof(*info), sizeof(*resp), &t); + if (ret) + return; + + info = t->tx.buf; + info->domain = cpu_to_le32(domain); + info->message_id = cpu_to_le32(message_id); + + ret = scmi_do_xfer(handle, t); + if (ret) + goto err_xfer; + + resp = t->rx.buf; + flags = le32_to_cpu(resp->attr); + size = le32_to_cpu(resp->chan_size); + if (!scmi_perf_fc_size_is_valid(message_id, size)) + goto err_xfer; + + phys_addr = le32_to_cpu(resp->chan_addr_low); + phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32; + addr = devm_ioremap(handle->dev, phys_addr, size); + if (!addr) + goto err_xfer; + *p_addr = addr; + + if (p_db && SUPPORTS_DOORBELL(flags)) { + db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL); + if (!db) + goto err_xfer; + + size = 1 << DOORBELL_REG_WIDTH(flags); + phys_addr = le32_to_cpu(resp->db_addr_low); + phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32; + addr = devm_ioremap(handle->dev, phys_addr, size); + if (!addr) + goto err_xfer; + + db->addr = addr; + db->width = size; + db->set = le32_to_cpu(resp->db_set_lmask); + db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32; + db->mask = le32_to_cpu(resp->db_preserve_lmask); + db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32; + *p_db = db; + } +err_xfer: + scmi_xfer_put(handle, t); +} + +static void scmi_perf_domain_init_fc(const struct scmi_handle *handle, + u32 domain, struct scmi_fc_info **p_fc) +{ + struct scmi_fc_info *fc; + + fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL); + if (!fc) + return; + + scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET, + &fc->level_set_addr, &fc->level_set_db); + scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET, + &fc->level_get_addr, NULL); + scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET, + &fc->limit_set_addr, &fc->limit_set_db); + scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET, + &fc->limit_get_addr, NULL); + *p_fc = fc; +} + /* Device specific ops */ static int scmi_dev_domain_id(struct device *dev) { @@ -494,6 +636,9 @@ static int scmi_perf_protocol_init(struct scmi_handle *handle) scmi_perf_domain_attributes_get(handle, domain, dom); scmi_perf_describe_levels_get(handle, domain, dom); + + if (dom->perf_fastchannels) + scmi_perf_domain_init_fc(handle, domain, &dom->fc_info); } handle->perf_ops = &perf_ops; From 823839571d76d465c6e454495864b085d699adb5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:17 +0100 Subject: [PATCH 055/123] firmware: arm_scmi: Make use SCMI v2.0 fastchannel for performance protocol SCMI v2.0 adds support for "FastChannel" which do not use a message header as they are specialized for a single message. Only PERFORMANCE_LIMITS_{SET,GET} and PERFORMANCE_LEVEL_{SET,GET} commands are supported over fastchannels. As they are optional, they need to be discovered by PERFORMANCE_DESCRIBE_FASTCHANNEL command. Further {LIMIT,LEVEL}_SET commands can have optional doorbell support. Add support for making use of these fastchannels. Cc: Ionela Voinescu Cc: Chris Redpath Cc: Quentin Perret Reviewed-by: Peng Fan Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 103 +++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index b0951b175578..4a8012e3cb8c 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -293,7 +294,41 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, +#define SCMI_PERF_FC_RING_DB(w) \ +do { \ + u##w val = 0; \ + \ + if (db->mask) \ + val = ioread##w(db->addr) & db->mask; \ + iowrite##w((u##w)db->set | val, db->addr); \ +} while (0) + +static void scmi_perf_fc_ring_db(struct scmi_fc_db_info *db) +{ + if (!db || !db->addr) + return; + + if (db->width == 1) + SCMI_PERF_FC_RING_DB(8); + else if (db->width == 2) + SCMI_PERF_FC_RING_DB(16); + else if (db->width == 4) + SCMI_PERF_FC_RING_DB(32); + else /* db->width == 8 */ +#ifdef CONFIG_64BIT + SCMI_PERF_FC_RING_DB(64); +#else + { + u64 val = 0; + + if (db->mask) + val = ioread64_hi_lo(db->addr) & db->mask; + iowrite64_hi_lo(db->set, db->addr); + } +#endif +} + +static int scmi_perf_mb_limits_set(const struct scmi_handle *handle, u32 domain, u32 max_perf, u32 min_perf) { int ret; @@ -316,7 +351,23 @@ static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, +static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain, + u32 max_perf, u32 min_perf) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->limit_set_addr) { + iowrite32(max_perf, dom->fc_info->limit_set_addr); + iowrite32(min_perf, dom->fc_info->limit_set_addr + 4); + scmi_perf_fc_ring_db(dom->fc_info->limit_set_db); + return 0; + } + + return scmi_perf_mb_limits_set(handle, domain, max_perf, min_perf); +} + +static int scmi_perf_mb_limits_get(const struct scmi_handle *handle, u32 domain, u32 *max_perf, u32 *min_perf) { int ret; @@ -342,7 +393,22 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, +static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain, + u32 *max_perf, u32 *min_perf) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->limit_get_addr) { + *max_perf = ioread32(dom->fc_info->limit_get_addr); + *min_perf = ioread32(dom->fc_info->limit_get_addr + 4); + return 0; + } + + return scmi_perf_mb_limits_get(handle, domain, max_perf, min_perf); +} + +static int scmi_perf_mb_level_set(const struct scmi_handle *handle, u32 domain, u32 level, bool poll) { int ret; @@ -365,7 +431,22 @@ static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, return ret; } -static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, +static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, + u32 level, bool poll) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->level_set_addr) { + iowrite32(level, dom->fc_info->level_set_addr); + scmi_perf_fc_ring_db(dom->fc_info->level_set_db); + return 0; + } + + return scmi_perf_mb_level_set(handle, domain, level, poll); +} + +static int scmi_perf_mb_level_get(const struct scmi_handle *handle, u32 domain, u32 *level, bool poll) { int ret; @@ -387,6 +468,20 @@ static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, return ret; } +static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, + u32 *level, bool poll) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom = pi->dom_info + domain; + + if (dom->fc_info && dom->fc_info->level_get_addr) { + *level = ioread32(dom->fc_info->level_get_addr); + return 0; + } + + return scmi_perf_mb_level_get(handle, domain, level, poll); +} + static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size) { if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4) From ae39913ce4095682d1a6e5734a5558062a59d29f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:04 +0100 Subject: [PATCH 056/123] dt-bindings: arm: Extend SCMI to support new reset protocol SCMIv2.0 adds a new Reset Management Protocol to manage various reset states a given device or domain can enter. Extend the existing SCMI bindings to add reset protocol support by re-using the reset bindings for both reset providers and consumers. Cc: Philipp Zabel Cc: Rob Herring Cc: Mark Rutland Cc: devicetree@vger.kernel.org Signed-off-by: Sudeep Holla --- .../devicetree/bindings/arm/arm,scmi.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt index 317a2fc3667a..083dbf96ee00 100644 --- a/Documentation/devicetree/bindings/arm/arm,scmi.txt +++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt @@ -73,6 +73,16 @@ Required properties: as used by the firmware. Refer to platform details for your implementation for the IDs to use. +Reset signal bindings for the reset domains based on SCMI Message Protocol +------------------------------------------------------------ + +This binding for the SCMI reset domain providers uses the generic reset +signal binding[5]. + +Required properties: + - #reset-cells : Should be 1. Contains the reset domain ID value used + by SCMI commands. + SRAM and Shared Memory for SCMI ------------------------------- @@ -93,6 +103,7 @@ Required sub-node properties: [2] Documentation/devicetree/bindings/power/power_domain.txt [3] Documentation/devicetree/bindings/thermal/thermal.txt [4] Documentation/devicetree/bindings/sram/sram.txt +[5] Documentation/devicetree/bindings/reset/reset.txt Example: @@ -152,6 +163,11 @@ firmware { reg = <0x15>; #thermal-sensor-cells = <1>; }; + + scmi_reset: protocol@16 { + reg = <0x16>; + #reset-cells = <1>; + }; }; }; @@ -166,6 +182,7 @@ hdlcd@7ff60000 { reg = <0 0x7ff60000 0 0x1000>; clocks = <&scmi_clk 4>; power-domains = <&scmi_devpd 1>; + resets = <&scmi_reset 10>; }; thermal-zones { From 95a15d80aa0de938299acfcbc6aa6f2b16f5d7e5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:06 +0100 Subject: [PATCH 057/123] firmware: arm_scmi: Add RESET protocol in SCMI v2.0 SCMIv2.0 adds a new Reset Management Protocol to manage various reset states a given device or domain can enter. Device(s) that can be collectively reset through a common reset signal constitute a reset domain for the firmware. A reset domain can be reset autonomously or explicitly through assertion and de-assertion of the signal. When autonomous reset is chosen, the firmware is responsible for taking the necessary steps to reset the domain and to subsequently bring it out of reset. When explicit reset is chosen, the caller has to specifically assert and then de-assert the reset signal by issuing two separate RESET commands. Add the basic SCMI reset infrastructure that can be used by Linux reset controller driver. Reviewed-by: Peng Fan Reviewed-by: Philipp Zabel Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/reset.c | 231 +++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 26 ++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/reset.c diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index c47d28d556b6..5f298f00a82e 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -2,5 +2,5 @@ obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-bus-y = bus.o scmi-driver-y = driver.o -scmi-protocols-y = base.o clock.o perf.o power.o sensors.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c new file mode 100644 index 000000000000..64cc81915581 --- /dev/null +++ b/drivers/firmware/arm_scmi/reset.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Reset Protocol + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include "common.h" + +enum scmi_reset_protocol_cmd { + RESET_DOMAIN_ATTRIBUTES = 0x3, + RESET = 0x4, + RESET_NOTIFY = 0x5, +}; + +enum scmi_reset_protocol_notify { + RESET_ISSUED = 0x0, +}; + +#define NUM_RESET_DOMAIN_MASK 0xffff +#define RESET_NOTIFY_ENABLE BIT(0) + +struct scmi_msg_resp_reset_domain_attributes { + __le32 attributes; +#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31)) +#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30)) + __le32 latency; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_reset_domain_reset { + __le32 domain_id; + __le32 flags; +#define AUTONOMOUS_RESET BIT(0) +#define EXPLICIT_RESET_ASSERT BIT(1) +#define ASYNCHRONOUS_RESET BIT(2) + __le32 reset_state; +#define ARCH_RESET_TYPE BIT(31) +#define COLD_RESET_STATE BIT(0) +#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE) +}; + +struct reset_dom_info { + bool async_reset; + bool reset_notify; + u32 latency_us; + char name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_reset_info { + int num_domains; + struct reset_dom_info *dom_info; +}; + +static int scmi_reset_attributes_get(const struct scmi_handle *handle, + struct scmi_reset_info *pi) +{ + int ret; + struct scmi_xfer *t; + u32 attr; + + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, + SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t); + if (ret) + return ret; + + ret = scmi_do_xfer(handle, t); + if (!ret) { + attr = get_unaligned_le32(t->rx.buf); + pi->num_domains = attr & NUM_RESET_DOMAIN_MASK; + } + + scmi_xfer_put(handle, t); + return ret; +} + +static int +scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain, + struct reset_dom_info *dom_info) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_resp_reset_domain_attributes *attr; + + ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES, + SCMI_PROTOCOL_RESET, sizeof(domain), + sizeof(*attr), &t); + if (ret) + return ret; + + put_unaligned_le32(domain, t->tx.buf); + attr = t->rx.buf; + + ret = scmi_do_xfer(handle, t); + if (!ret) { + u32 attributes = le32_to_cpu(attr->attributes); + + dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); + dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes); + dom_info->latency_us = le32_to_cpu(attr->latency); + if (dom_info->latency_us == U32_MAX) + dom_info->latency_us = 0; + strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); + } + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_reset_num_domains_get(const struct scmi_handle *handle) +{ + struct scmi_reset_info *pi = handle->reset_priv; + + return pi->num_domains; +} + +static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain) +{ + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->name; +} + +static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain) +{ + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->latency_us; +} + +static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain, + u32 flags, u32 state) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_reset_domain_reset *dom; + struct scmi_reset_info *pi = handle->reset_priv; + struct reset_dom_info *rdom = pi->dom_info + domain; + + if (rdom->async_reset) + flags |= ASYNCHRONOUS_RESET; + + ret = scmi_xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET, + sizeof(*dom), 0, &t); + if (ret) + return ret; + + dom = t->tx.buf; + dom->domain_id = cpu_to_le32(domain); + dom->flags = cpu_to_le32(flags); + dom->domain_id = cpu_to_le32(state); + + if (rdom->async_reset) + ret = scmi_do_xfer_with_response(handle, t); + else + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_reset_domain_reset(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, AUTONOMOUS_RESET, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_assert(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, EXPLICIT_RESET_ASSERT, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain) +{ + return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET); +} + +static struct scmi_reset_ops reset_ops = { + .num_domains_get = scmi_reset_num_domains_get, + .name_get = scmi_reset_name_get, + .latency_get = scmi_reset_latency_get, + .reset = scmi_reset_domain_reset, + .assert = scmi_reset_domain_assert, + .deassert = scmi_reset_domain_deassert, +}; + +static int scmi_reset_protocol_init(struct scmi_handle *handle) +{ + int domain; + u32 version; + struct scmi_reset_info *pinfo; + + scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version); + + dev_dbg(handle->dev, "Reset Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + scmi_reset_attributes_get(handle, pinfo); + + pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains, + sizeof(*pinfo->dom_info), GFP_KERNEL); + if (!pinfo->dom_info) + return -ENOMEM; + + for (domain = 0; domain < pinfo->num_domains; domain++) { + struct reset_dom_info *dom = pinfo->dom_info + domain; + + scmi_reset_domain_attributes_get(handle, domain, dom); + } + + handle->reset_ops = &reset_ops; + handle->reset_priv = pinfo; + + return 0; +} + +static int __init scmi_reset_init(void) +{ + return scmi_protocol_register(SCMI_PROTOCOL_RESET, + &scmi_reset_protocol_init); +} +subsys_initcall(scmi_reset_init); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index f0f2b53a1dac..881fea47c83d 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -187,6 +187,26 @@ struct scmi_sensor_ops { u64 *value); }; +/** + * struct scmi_reset_ops - represents the various operations provided + * by SCMI Reset Protocol + * + * @num_domains_get: get the count of reset domains provided by SCMI + * @name_get: gets the name of a reset domain + * @latency_get: gets the reset latency for the specified reset domain + * @reset: resets the specified reset domain + * @assert: explicitly assert reset signal of the specified reset domain + * @deassert: explicitly deassert reset signal of the specified reset domain + */ +struct scmi_reset_ops { + int (*num_domains_get)(const struct scmi_handle *handle); + char *(*name_get)(const struct scmi_handle *handle, u32 domain); + int (*latency_get)(const struct scmi_handle *handle, u32 domain); + int (*reset)(const struct scmi_handle *handle, u32 domain); + int (*assert)(const struct scmi_handle *handle, u32 domain); + int (*deassert)(const struct scmi_handle *handle, u32 domain); +}; + /** * struct scmi_handle - Handle returned to ARM SCMI clients for usage. * @@ -196,6 +216,7 @@ struct scmi_sensor_ops { * @perf_ops: pointer to set of performance protocol operations * @clk_ops: pointer to set of clock protocol operations * @sensor_ops: pointer to set of sensor protocol operations + * @reset_ops: pointer to set of reset protocol operations * @perf_priv: pointer to private data structure specific to performance * protocol(for internal use only) * @clk_priv: pointer to private data structure specific to clock @@ -204,6 +225,8 @@ struct scmi_sensor_ops { * protocol(for internal use only) * @sensor_priv: pointer to private data structure specific to sensors * protocol(for internal use only) + * @reset_priv: pointer to private data structure specific to reset + * protocol(for internal use only) */ struct scmi_handle { struct device *dev; @@ -212,11 +235,13 @@ struct scmi_handle { struct scmi_clk_ops *clk_ops; struct scmi_power_ops *power_ops; struct scmi_sensor_ops *sensor_ops; + struct scmi_reset_ops *reset_ops; /* for protocol internal use */ void *perf_priv; void *clk_priv; void *power_priv; void *sensor_priv; + void *reset_priv; }; enum scmi_std_protocol { @@ -226,6 +251,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_PERF = 0x13, SCMI_PROTOCOL_CLOCK = 0x14, SCMI_PROTOCOL_SENSOR = 0x15, + SCMI_PROTOCOL_RESET = 0x16, }; struct scmi_device { From c8ae9c2da1cc5d18b6d51d10160508a3dc3436bf Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 8 Jul 2019 09:41:08 +0100 Subject: [PATCH 058/123] reset: Add support for resets provided by SCMI On some ARM based systems, a separate Cortex-M based System Control Processor(SCP) provides the overall power, clock, reset and system control. System Control and Management Interface(SCMI) Message Protocol is defined for the communication between the Application Cores(AP) and the SCP. Adds support for the resets provided using SCMI protocol for performing reset management of various devices present on the SoC. Various reset functionalities are achieved by the means of different ARM SCMI device operations provided by the ARM SCMI framework. Reviewed-by: Philipp Zabel Signed-off-by: Sudeep Holla --- MAINTAINERS | 1 + drivers/reset/Kconfig | 11 ++++ drivers/reset/Makefile | 1 + drivers/reset/reset-scmi.c | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 drivers/reset/reset-scmi.c diff --git a/MAINTAINERS b/MAINTAINERS index 783569e3c4b4..59df8f88b56d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15545,6 +15545,7 @@ F: drivers/clk/clk-sc[mp]i.c F: drivers/cpufreq/sc[mp]i-cpufreq.c F: drivers/firmware/arm_scpi.c F: drivers/firmware/arm_scmi/ +F: drivers/reset/reset-scmi.c F: include/linux/sc[mp]i_protocol.h SYSTEM RESET/SHUTDOWN DRIVERS diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 21efb7d39d62..4178ac11ba85 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -116,6 +116,17 @@ config RESET_QCOM_PDC to control reset signals provided by PDC for Modem, Compute, Display, GPU, Debug, AOP, Sensors, Audio, SP and APPS. +config RESET_SCMI + tristate "Reset driver controlled via ARM SCMI interface" + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST + default ARM_SCMI_PROTOCOL + help + This driver provides support for reset signal/domains that are + controlled by firmware that implements the SCMI interface. + + This driver uses SCMI Message Protocol to interact with the + firmware controlling all the reset signals. + config RESET_SIMPLE bool "Simple Reset Controller Driver" if COMPILE_TEST default ARCH_STM32 || ARCH_STRATIX10 || ARCH_SUNXI || ARCH_ZX || ARCH_ASPEED || ARCH_BITMAIN diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 61456b8f659c..cf60ce526064 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_QCOM_PDC) += reset-qcom-pdc.o +obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c new file mode 100644 index 000000000000..c6d3c8427f14 --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM System Control and Management Interface (ARM SCMI) reset driver + * + * Copyright (C) 2019 ARM Ltd. + */ + +#include +#include +#include +#include +#include + +/** + * struct scmi_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @handle: ARM SCMI handle used for communication with system controller + */ +struct scmi_reset_data { + struct reset_controller_dev rcdev; + const struct scmi_handle *handle; +}; + +#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev) +#define to_scmi_handle(p) (to_scmi_reset_data(p)->handle) + +/** + * scmi_reset_assert() - assert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be asserted + * + * This function implements the reset driver op to assert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->assert(handle, id); +} + +/** + * scmi_reset_deassert() - deassert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be deasserted + * + * This function implements the reset driver op to deassert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->deassert(handle, id); +} + +/** + * scmi_reset_reset() - reset the device + * @rcdev: reset controller entity + * @id: ID of the reset signal to be reset(assert + deassert) + * + * This function implements the reset driver op to trigger a device's + * reset signal using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_handle *handle = to_scmi_handle(rcdev); + + return handle->reset_ops->reset(handle, id); +} + +static const struct reset_control_ops scmi_reset_ops = { + .assert = scmi_reset_assert, + .deassert = scmi_reset_deassert, + .reset = scmi_reset_reset, +}; + +static int scmi_reset_probe(struct scmi_device *sdev) +{ + struct scmi_reset_data *data; + struct device *dev = &sdev->dev; + struct device_node *np = dev->of_node; + const struct scmi_handle *handle = sdev->handle; + + if (!handle || !handle->reset_ops) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &scmi_reset_ops; + data->rcdev.owner = THIS_MODULE; + data->rcdev.of_node = np; + data->rcdev.nr_resets = handle->reset_ops->num_domains_get(handle); + + return devm_reset_controller_register(dev, &data->rcdev); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_RESET }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_reset_driver = { + .name = "scmi-reset", + .probe = scmi_reset_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_reset_driver); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM SCMI reset controller driver"); +MODULE_LICENSE("GPL v2"); From 73feb4d0f8f14c5102bd46ce2255ff55d3d52db7 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 2 Jul 2019 15:45:45 +0800 Subject: [PATCH 059/123] soc: imx-scu: Add SoC UID(unique identifier) support Add i.MX SCU SoC's UID(unique identifier) support, user can read it from sysfs: root@imx8qxpmek:~# cat /sys/devices/soc0/soc_uid 7B64280B57AC1898 Signed-off-by: Anson Huang Reviewed-by: Daniel Baluta Signed-off-by: Shawn Guo --- drivers/soc/imx/soc-imx-scu.c | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/soc/imx/soc-imx-scu.c b/drivers/soc/imx/soc-imx-scu.c index 676f612f6488..50831ebf126a 100644 --- a/drivers/soc/imx/soc-imx-scu.c +++ b/drivers/soc/imx/soc-imx-scu.c @@ -27,6 +27,40 @@ struct imx_sc_msg_misc_get_soc_id { } data; } __packed; +struct imx_sc_msg_misc_get_soc_uid { + struct imx_sc_rpc_msg hdr; + u32 uid_low; + u32 uid_high; +} __packed; + +static ssize_t soc_uid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct imx_sc_msg_misc_get_soc_uid msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u64 soc_uid; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_MISC; + hdr->func = IMX_SC_MISC_FUNC_UNIQUE_ID; + hdr->size = 1; + + ret = imx_scu_call_rpc(soc_ipc_handle, &msg, false); + if (ret) { + pr_err("%s: get soc uid failed, ret %d\n", __func__, ret); + return ret; + } + + soc_uid = msg.uid_high; + soc_uid <<= 32; + soc_uid |= msg.uid_low; + + return sprintf(buf, "%016llX\n", soc_uid); +} + +static DEVICE_ATTR_RO(soc_uid); + static int imx_scu_soc_id(void) { struct imx_sc_msg_misc_get_soc_id msg; @@ -102,6 +136,11 @@ static int imx_scu_soc_probe(struct platform_device *pdev) goto free_revision; } + ret = device_create_file(soc_device_to_device(soc_dev), + &dev_attr_soc_uid); + if (ret) + goto free_revision; + return 0; free_revision: From ffbf23d50353915dc2622a3b7b4ddc678165f92d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 1 Aug 2019 12:56:36 +0300 Subject: [PATCH 060/123] firmware: imx: Add DSP IPC protocol interface Some of i.MX8 processors (e.g i.MX8QM, i.MX8QXP) contain the Tensilica HiFi4 DSP for advanced pre- and post-audio processing. The communication between Host CPU and DSP firmware is taking place using a shared memory area for message passing and a dedicated Messaging Unit for notifications. DSP IPC protocol offers a doorbell interface using imx-mailbox API. We use 4 MU channels (2 x TXDB, 2 x RXDB) to implement a request-reply protocol. Connection 0 (txdb0, rxdb0): - Host writes messasge to shared memory [SHMEM] - Host sends a request [MU] - DSP handles request [SHMEM] - DSP sends reply [MU] Connection 1 (txdb1, rxdb1): - DSP writes a message to shared memory [SHMEM] - DSP sends a request [MU] - Host handles request [SHMEM] - Host sends reply [MU] The protocol interface will be used by a Host client to communicate with the DSP. First client will be the i.MX8 part from Sound Open Firmware infrastructure. The protocol offers the following interface: On Tx: - imx_dsp_ring_doorbell, will be called to notify the DSP that it needs to handle a request. On Rx: - clients need to provide two callbacks: .handle_reply .handle_request - the callbacks will be used by the protocol on notification arrival from DSP. Signed-off-by: Daniel Baluta Reviewed-by: Oleksij Rempel Signed-off-by: Shawn Guo --- drivers/firmware/imx/Kconfig | 11 +++ drivers/firmware/imx/Makefile | 1 + drivers/firmware/imx/imx-dsp.c | 155 +++++++++++++++++++++++++++++++ include/linux/firmware/imx/dsp.h | 67 +++++++++++++ 4 files changed, 234 insertions(+) create mode 100644 drivers/firmware/imx/imx-dsp.c create mode 100644 include/linux/firmware/imx/dsp.h diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index 42b566f8903f..0dbee32da4c6 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -1,4 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-only +config IMX_DSP + bool "IMX DSP Protocol driver" + depends on IMX_MBOX + help + This enables DSP IPC protocol between host AP (Linux) + and the firmware running on DSP. + DSP exists on some i.MX8 processors (e.g i.MX8QM, i.MX8QXP). + + It acts like a doorbell. Client might use shared memory to + exchange information with DSP side. + config IMX_SCU bool "IMX SCU Protocol driver" depends on IMX_MBOX diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 802c4ad8e8f9..08bc9ddfbdfb 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_IMX_DSP) += imx-dsp.o obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c new file mode 100644 index 000000000000..a43d2db5cbdb --- /dev/null +++ b/drivers/firmware/imx/imx-dsp.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * Author: Daniel Baluta + * + * Implementation of the DSP IPC interface (host side) + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * imx_dsp_ring_doorbell - triggers an interrupt on the other side (DSP) + * + * @dsp: DSP IPC handle + * @chan_idx: index of the channel where to trigger the interrupt + * + * Returns non-negative value for success, negative value for error + */ +int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, unsigned int idx) +{ + int ret; + struct imx_dsp_chan *dsp_chan; + + if (idx >= DSP_MU_CHAN_NUM) + return -EINVAL; + + dsp_chan = &ipc->chans[idx]; + ret = mbox_send_message(dsp_chan->ch, NULL); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(imx_dsp_ring_doorbell); + +/* + * imx_dsp_handle_rx - rx callback used by imx mailbox + * + * @c: mbox client + * @msg: message received + * + * Users of DSP IPC will need to privde handle_reply and handle_request + * callbacks. + */ +static void imx_dsp_handle_rx(struct mbox_client *c, void *msg) +{ + struct imx_dsp_chan *chan = container_of(c, struct imx_dsp_chan, cl); + + if (chan->idx == 0) { + chan->ipc->ops->handle_reply(chan->ipc); + } else { + chan->ipc->ops->handle_request(chan->ipc); + imx_dsp_ring_doorbell(chan->ipc, 1); + } +} + +static int imx_dsp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_dsp_ipc *dsp_ipc; + struct imx_dsp_chan *dsp_chan; + struct mbox_client *cl; + char *chan_name; + int ret; + int i, j; + + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); + + dsp_ipc = devm_kzalloc(dev, sizeof(*dsp_ipc), GFP_KERNEL); + if (!dsp_ipc) + return -ENOMEM; + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { + if (i < 2) + chan_name = kasprintf(GFP_KERNEL, "txdb%d", i); + else + chan_name = kasprintf(GFP_KERNEL, "rxdb%d", i - 2); + + if (!chan_name) + return -ENOMEM; + + dsp_chan = &dsp_ipc->chans[i]; + cl = &dsp_chan->cl; + cl->dev = dev; + cl->tx_block = false; + cl->knows_txdone = true; + cl->rx_callback = imx_dsp_handle_rx; + + dsp_chan->ipc = dsp_ipc; + dsp_chan->idx = i % 2; + dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); + if (IS_ERR(dsp_chan->ch)) { + ret = PTR_ERR(dsp_chan->ch); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to request mbox chan %s ret %d\n", + chan_name, ret); + goto out; + } + + dev_dbg(dev, "request mbox chan %s\n", chan_name); + /* chan_name is not used anymore by framework */ + kfree(chan_name); + } + + dsp_ipc->dev = dev; + + dev_set_drvdata(dev, dsp_ipc); + + dev_info(dev, "NXP i.MX DSP IPC initialized\n"); + + return devm_of_platform_populate(dev); +out: + kfree(chan_name); + for (j = 0; j < i; j++) { + dsp_chan = &dsp_ipc->chans[j]; + mbox_free_channel(dsp_chan->ch); + } + + return ret; +} + +static int imx_dsp_remove(struct platform_device *pdev) +{ + struct imx_dsp_chan *dsp_chan; + struct imx_dsp_ipc *dsp_ipc; + int i; + + dsp_ipc = dev_get_drvdata(&pdev->dev); + + for (i = 0; i < DSP_MU_CHAN_NUM; i++) { + dsp_chan = &dsp_ipc->chans[i]; + mbox_free_channel(dsp_chan->ch); + } + + return 0; +} + +static struct platform_driver imx_dsp_driver = { + .driver = { + .name = "imx-dsp", + }, + .probe = imx_dsp_probe, + .remove = imx_dsp_remove, +}; +builtin_platform_driver(imx_dsp_driver); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("IMX DSP IPC protocol driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/firmware/imx/dsp.h b/include/linux/firmware/imx/dsp.h new file mode 100644 index 000000000000..7562099c9e46 --- /dev/null +++ b/include/linux/firmware/imx/dsp.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2019 NXP + * + * Header file for the DSP IPC implementation + */ + +#ifndef _IMX_DSP_IPC_H +#define _IMX_DSP_IPC_H + +#include +#include +#include + +#define DSP_MU_CHAN_NUM 4 + +struct imx_dsp_chan { + struct imx_dsp_ipc *ipc; + struct mbox_client cl; + struct mbox_chan *ch; + char *name; + int idx; +}; + +struct imx_dsp_ops { + void (*handle_reply)(struct imx_dsp_ipc *ipc); + void (*handle_request)(struct imx_dsp_ipc *ipc); +}; + +struct imx_dsp_ipc { + /* Host <-> DSP communication uses 2 txdb and 2 rxdb channels */ + struct imx_dsp_chan chans[DSP_MU_CHAN_NUM]; + struct device *dev; + struct imx_dsp_ops *ops; + void *private_data; +}; + +static inline void imx_dsp_set_data(struct imx_dsp_ipc *ipc, void *data) +{ + if (!ipc) + return; + + ipc->private_data = data; +} + +static inline void *imx_dsp_get_data(struct imx_dsp_ipc *ipc) +{ + if (!ipc) + return NULL; + + return ipc->private_data; +} + +#if IS_ENABLED(CONFIG_IMX_DSP) + +int imx_dsp_ring_doorbell(struct imx_dsp_ipc *dsp, unsigned int chan_idx); + +#else + +static inline int imx_dsp_ring_doorbell(struct imx_dsp_ipc *ipc, + unsigned int chan_idx) +{ + return -ENOTSUPP; +} + +#endif +#endif /* _IMX_DSP_IPC_H */ From 5bc7f990cd98f3f7977d62bfe75c50b4b33d13f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 12 Aug 2019 18:11:14 +0200 Subject: [PATCH 061/123] bus: Add support for Moxtet bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On the Turris Mox router different modules can be connected to the main CPU board: currently a module with a SFP cage, a module with MiniPCIe connector, a PCIe pass-through MiniPCIe connector module, a 4-port switch module, an 8-port switch module, and a 4-port USB3 module. For example: [CPU]-[PCIe-pass-through]-[PCIe]-[8-port switch]-[8-port switch]-[SFP] Each of this modules has an input and output shift register, and these are connected via SPI to the CPU board. Via SPI we are able to discover which modules are connected, in which order, and we can also read some information about the modules (eg. their interrupt status), and configure them. From each module 8 bits can be read (of which low 4 bits identify the module) and 8 bits can be written. For example from the module with a SFP cage we can read the LOS, TX-FAULT and MOD-DEF0 signals, while we can write TX-DISABLE and RATE-SELECT signals. This driver creates a new bus type, called "moxtet". For each Mox module it finds via SPI, it creates a new device on the moxtet bus so that drivers can be written for them. It also implements a virtual interrupt controller for the modules which send their interrupt status over the SPI shift register. These modules do this in addition to sending their interrupt status via the shared interrupt line. When the shared interrupt is triggered, we read from the shift register and handle IRQs for all devices which are in interrupt. The topology of how Mox modules are connected can then be read by listing /sys/bus/moxtet/devices. Link: https://lore.kernel.org/r/20190812161118.21476-2-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- MAINTAINERS | 7 + drivers/bus/Kconfig | 10 + drivers/bus/Makefile | 1 + drivers/bus/moxtet.c | 886 +++++++++++++++++++++++++++++++ include/dt-bindings/bus/moxtet.h | 16 + include/linux/moxtet.h | 109 ++++ 6 files changed, 1029 insertions(+) create mode 100644 drivers/bus/moxtet.c create mode 100644 include/dt-bindings/bus/moxtet.h create mode 100644 include/linux/moxtet.h diff --git a/MAINTAINERS b/MAINTAINERS index 783569e3c4b4..01309a228593 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1626,6 +1626,13 @@ F: drivers/clocksource/timer-atlas7.c N: [^a-z]sirf X: drivers/gnss +ARM/CZ.NIC TURRIS MOX SUPPORT +M: Marek Behun +W: http://mox.turris.cz +S: Maintained +F: include/linux/moxtet.h +F: drivers/bus/moxtet.c + ARM/EBSA110 MACHINE SUPPORT M: Russell King L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 1851112ccc29..6b331061d34b 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -29,6 +29,16 @@ config BRCMSTB_GISB_ARB arbiter. This driver provides timeout and target abort error handling and internal bus master decoding. +config MOXTET + tristate "CZ.NIC Turris Mox module configuration bus" + depends on SPI_MASTER && OF + help + Say yes here to add support for the module configuration bus found + on CZ.NIC's Turris Mox. This is needed for the ability to discover + the order in which the modules are connected and to get/set some of + their settings. For example the GPIOs on Mox SFP module are + configured through this bus. + config HISILICON_LPC bool "Support for ISA I/O space on HiSilicon Hip06/7" depends on ARM64 && (ARCH_HISI || COMPILE_TEST) diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ca300b1914ce..16b43d3468c6 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o +obj-$(CONFIG_MOXTET) += moxtet.o # DPAA2 fsl-mc bus obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c new file mode 100644 index 000000000000..1ee4570e7e17 --- /dev/null +++ b/drivers/bus/moxtet.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox module configuration bus driver + * + * Copyright (C) 2019 Marek Behun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * @name: module name for sysfs + * @hwirq_base: base index for IRQ for this module (-1 if no IRQs) + * @nirqs: how many interrupts does the shift register provide + * @desc: module description for kernel log + */ +static const struct { + const char *name; + int hwirq_base; + int nirqs; + const char *desc; +} mox_module_table[] = { + /* do not change order of this array! */ + { NULL, 0, 0, NULL }, + { "sfp", -1, 0, "MOX D (SFP cage)" }, + { "pci", MOXTET_IRQ_PCI, 1, "MOX B (Mini-PCIe)" }, + { "topaz", MOXTET_IRQ_TOPAZ, 1, "MOX C (4 port switch)" }, + { "peridot", MOXTET_IRQ_PERIDOT(0), 1, "MOX E (8 port switch)" }, + { "usb3", MOXTET_IRQ_USB3, 2, "MOX F (USB 3.0)" }, + { "pci-bridge", -1, 0, "MOX G (Mini-PCIe bridge)" }, +}; + +static inline bool mox_module_known(unsigned int id) +{ + return id >= TURRIS_MOX_MODULE_FIRST && id <= TURRIS_MOX_MODULE_LAST; +} + +static inline const char *mox_module_name(unsigned int id) +{ + if (mox_module_known(id)) + return mox_module_table[id].name; + else + return "unknown"; +} + +#define DEF_MODULE_ATTR(name, fmt, ...) \ +static ssize_t \ +module_##name##_show(struct device *dev, struct device_attribute *a, \ + char *buf) \ +{ \ + struct moxtet_device *mdev = to_moxtet_device(dev); \ + return sprintf(buf, (fmt), __VA_ARGS__); \ +} \ +static DEVICE_ATTR_RO(module_##name) + +DEF_MODULE_ATTR(id, "0x%x\n", mdev->id); +DEF_MODULE_ATTR(name, "%s\n", mox_module_name(mdev->id)); +DEF_MODULE_ATTR(description, "%s\n", + mox_module_known(mdev->id) ? mox_module_table[mdev->id].desc + : ""); + +static struct attribute *moxtet_dev_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_module_name.attr, + &dev_attr_module_description.attr, + NULL, +}; + +static const struct attribute_group moxtet_dev_group = { + .attrs = moxtet_dev_attrs, +}; + +static const struct attribute_group *moxtet_dev_groups[] = { + &moxtet_dev_group, + NULL, +}; + +static int moxtet_match(struct device *dev, struct device_driver *drv) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet_driver *tdrv = to_moxtet_driver(drv); + const enum turris_mox_module_id *t; + + if (of_driver_match_device(dev, drv)) + return 1; + + if (!tdrv->id_table) + return 0; + + for (t = tdrv->id_table; *t; ++t) + if (*t == mdev->id) + return 1; + + return 0; +} + +struct bus_type moxtet_bus_type = { + .name = "moxtet", + .dev_groups = moxtet_dev_groups, + .match = moxtet_match, +}; +EXPORT_SYMBOL_GPL(moxtet_bus_type); + +int __moxtet_register_driver(struct module *owner, + struct moxtet_driver *mdrv) +{ + mdrv->driver.owner = owner; + mdrv->driver.bus = &moxtet_bus_type; + return driver_register(&mdrv->driver); +} +EXPORT_SYMBOL_GPL(__moxtet_register_driver); + +static int moxtet_dev_check(struct device *dev, void *data) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet_device *new_dev = data; + + if (mdev->moxtet == new_dev->moxtet && mdev->id == new_dev->id && + mdev->idx == new_dev->idx) + return -EBUSY; + return 0; +} + +static void moxtet_dev_release(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + + put_device(mdev->moxtet->dev); + kfree(mdev); +} + +static struct moxtet_device * +moxtet_alloc_device(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + + if (!get_device(moxtet->dev)) + return NULL; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + put_device(moxtet->dev); + return NULL; + } + + dev->moxtet = moxtet; + dev->dev.parent = moxtet->dev; + dev->dev.bus = &moxtet_bus_type; + dev->dev.release = moxtet_dev_release; + + device_initialize(&dev->dev); + + return dev; +} + +static int moxtet_add_device(struct moxtet_device *dev) +{ + static DEFINE_MUTEX(add_mutex); + int ret; + + if (dev->idx >= TURRIS_MOX_MAX_MODULES || dev->id > 0xf) + return -EINVAL; + + dev_set_name(&dev->dev, "moxtet-%s.%u", mox_module_name(dev->id), + dev->idx); + + mutex_lock(&add_mutex); + + ret = bus_for_each_dev(&moxtet_bus_type, NULL, dev, + moxtet_dev_check); + if (ret) + goto done; + + ret = device_add(&dev->dev); + if (ret < 0) + dev_err(dev->moxtet->dev, "can't add %s, status %d\n", + dev_name(dev->moxtet->dev), ret); + +done: + mutex_unlock(&add_mutex); + return ret; +} + +static int __unregister(struct device *dev, void *null) +{ + if (dev->of_node) { + of_node_clear_flag(dev->of_node, OF_POPULATED); + of_node_put(dev->of_node); + } + + device_unregister(dev); + + return 0; +} + +static struct moxtet_device * +of_register_moxtet_device(struct moxtet *moxtet, struct device_node *nc) +{ + struct moxtet_device *dev; + u32 val; + int ret; + + dev = moxtet_alloc_device(moxtet); + if (!dev) { + dev_err(moxtet->dev, + "Moxtet device alloc error for %pOF\n", nc); + return ERR_PTR(-ENOMEM); + } + + ret = of_property_read_u32(nc, "reg", &val); + if (ret) { + dev_err(moxtet->dev, "%pOF has no valid 'reg' property (%d)\n", + nc, ret); + goto err_put; + } + + dev->idx = val; + + if (dev->idx >= TURRIS_MOX_MAX_MODULES) { + dev_err(moxtet->dev, "%pOF Moxtet address 0x%x out of range\n", + nc, dev->idx); + ret = -EINVAL; + goto err_put; + } + + dev->id = moxtet->modules[dev->idx]; + + if (!dev->id) { + dev_err(moxtet->dev, "%pOF Moxtet address 0x%x is empty\n", nc, + dev->idx); + ret = -ENODEV; + goto err_put; + } + + of_node_get(nc); + dev->dev.of_node = nc; + + ret = moxtet_add_device(dev); + if (ret) { + dev_err(moxtet->dev, + "Moxtet device register error for %pOF\n", nc); + of_node_put(nc); + goto err_put; + } + + return dev; + +err_put: + put_device(&dev->dev); + return ERR_PTR(ret); +} + +static void of_register_moxtet_devices(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + struct device_node *nc; + + if (!moxtet->dev->of_node) + return; + + for_each_available_child_of_node(moxtet->dev->of_node, nc) { + if (of_node_test_and_set_flag(nc, OF_POPULATED)) + continue; + dev = of_register_moxtet_device(moxtet, nc); + if (IS_ERR(dev)) { + dev_warn(moxtet->dev, + "Failed to create Moxtet device for %pOF\n", + nc); + of_node_clear_flag(nc, OF_POPULATED); + } + } +} + +static void +moxtet_register_devices_from_topology(struct moxtet *moxtet) +{ + struct moxtet_device *dev; + int i, ret; + + for (i = 0; i < moxtet->count; ++i) { + dev = moxtet_alloc_device(moxtet); + if (!dev) { + dev_err(moxtet->dev, "Moxtet device %u alloc error\n", + i); + continue; + } + + dev->idx = i; + dev->id = moxtet->modules[i]; + + ret = moxtet_add_device(dev); + if (ret && ret != -EBUSY) { + put_device(&dev->dev); + dev_err(moxtet->dev, + "Moxtet device %u register error: %i\n", i, + ret); + } + } +} + +/* + * @nsame: how many modules with same id are already in moxtet->modules + */ +static int moxtet_set_irq(struct moxtet *moxtet, int idx, int id, int nsame) +{ + int i, first; + struct moxtet_irqpos *pos; + + first = mox_module_table[id].hwirq_base + + nsame * mox_module_table[id].nirqs; + + if (first + mox_module_table[id].nirqs > MOXTET_NIRQS) + return -EINVAL; + + for (i = 0; i < mox_module_table[id].nirqs; ++i) { + pos = &moxtet->irq.position[first + i]; + pos->idx = idx; + pos->bit = i; + moxtet->irq.exists |= BIT(first + i); + } + + return 0; +} + +static int moxtet_find_topology(struct moxtet *moxtet) +{ + u8 buf[TURRIS_MOX_MAX_MODULES]; + int cnts[TURRIS_MOX_MODULE_LAST]; + int i, ret; + + memset(cnts, 0, sizeof(cnts)); + + ret = spi_read(to_spi_device(moxtet->dev), buf, TURRIS_MOX_MAX_MODULES); + if (ret < 0) + return ret; + + if (buf[0] == TURRIS_MOX_CPU_ID_EMMC) { + dev_info(moxtet->dev, "Found MOX A (eMMC CPU) module\n"); + } else if (buf[0] == TURRIS_MOX_CPU_ID_SD) { + dev_info(moxtet->dev, "Found MOX A (CPU) module\n"); + } else { + dev_err(moxtet->dev, "Invalid Turris MOX A CPU module 0x%02x\n", + buf[0]); + return -ENODEV; + } + + moxtet->count = 0; + + for (i = 1; i < TURRIS_MOX_MAX_MODULES; ++i) { + int id; + + if (buf[i] == 0xff) + break; + + id = buf[i] & 0xf; + + moxtet->modules[i-1] = id; + ++moxtet->count; + + if (mox_module_known(id)) { + dev_info(moxtet->dev, "Found %s module\n", + mox_module_table[id].desc); + + if (moxtet_set_irq(moxtet, i-1, id, cnts[id]++) < 0) + dev_err(moxtet->dev, + " Cannot set IRQ for module %s\n", + mox_module_table[id].desc); + } else { + dev_warn(moxtet->dev, + "Unknown Moxtet module found (ID 0x%02x)\n", + id); + } + } + + return 0; +} + +static int moxtet_spi_read(struct moxtet *moxtet, u8 *buf) +{ + struct spi_transfer xfer = { + .rx_buf = buf, + .tx_buf = moxtet->tx, + .len = moxtet->count + 1 + }; + int ret; + + mutex_lock(&moxtet->lock); + + ret = spi_sync_transfer(to_spi_device(moxtet->dev), &xfer, 1); + + mutex_unlock(&moxtet->lock); + + return ret; +} + +int moxtet_device_read(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + u8 buf[TURRIS_MOX_MAX_MODULES]; + int ret; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + ret = moxtet_spi_read(moxtet, buf); + if (ret < 0) + return ret; + + return buf[mdev->idx + 1] >> 4; +} +EXPORT_SYMBOL_GPL(moxtet_device_read); + +int moxtet_device_write(struct device *dev, u8 val) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + int ret; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + mutex_lock(&moxtet->lock); + + moxtet->tx[moxtet->count - mdev->idx] = val; + + ret = spi_write(to_spi_device(moxtet->dev), moxtet->tx, + moxtet->count + 1); + + mutex_unlock(&moxtet->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(moxtet_device_write); + +int moxtet_device_written(struct device *dev) +{ + struct moxtet_device *mdev = to_moxtet_device(dev); + struct moxtet *moxtet = mdev->moxtet; + + if (mdev->idx >= moxtet->count) + return -EINVAL; + + return moxtet->tx[moxtet->count - mdev->idx]; +} +EXPORT_SYMBOL_GPL(moxtet_device_written); + +#ifdef CONFIG_DEBUG_FS +static int moxtet_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return nonseekable_open(inode, file); +} + +static ssize_t input_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 bin[TURRIS_MOX_MAX_MODULES]; + u8 hex[sizeof(buf) * 2 + 1]; + int ret, n; + + ret = moxtet_spi_read(moxtet, bin); + if (ret < 0) + return ret; + + n = moxtet->count + 1; + bin2hex(hex, bin, n); + + hex[2*n] = '\n'; + + return simple_read_from_buffer(buf, len, ppos, hex, 2*n + 1); +} + +static const struct file_operations input_fops = { + .owner = THIS_MODULE, + .open = moxtet_debug_open, + .read = input_read, + .llseek = no_llseek, +}; + +static ssize_t output_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 hex[TURRIS_MOX_MAX_MODULES * 2 + 1]; + u8 *p = hex; + int i; + + mutex_lock(&moxtet->lock); + + for (i = 0; i < moxtet->count; ++i) + p = hex_byte_pack(p, moxtet->tx[moxtet->count - i]); + + mutex_unlock(&moxtet->lock); + + *p++ = '\n'; + + return simple_read_from_buffer(buf, len, ppos, hex, p - hex); +} + +static ssize_t output_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct moxtet *moxtet = file->private_data; + u8 bin[TURRIS_MOX_MAX_MODULES]; + u8 hex[sizeof(bin) * 2 + 1]; + size_t res; + loff_t dummy = 0; + int err, i; + + if (len > 2 * moxtet->count + 1 || len < 2 * moxtet->count) + return -EINVAL; + + res = simple_write_to_buffer(hex, sizeof(hex), &dummy, buf, len); + if (res < 0) + return res; + + if (len % 2 == 1 && hex[len - 1] != '\n') + return -EINVAL; + + err = hex2bin(bin, hex, moxtet->count); + if (err < 0) + return -EINVAL; + + mutex_lock(&moxtet->lock); + + for (i = 0; i < moxtet->count; ++i) + moxtet->tx[moxtet->count - i] = bin[i]; + + err = spi_write(to_spi_device(moxtet->dev), moxtet->tx, + moxtet->count + 1); + + mutex_unlock(&moxtet->lock); + + return err < 0 ? err : len; +} + +static const struct file_operations output_fops = { + .owner = THIS_MODULE, + .open = moxtet_debug_open, + .read = output_read, + .write = output_write, + .llseek = no_llseek, +}; + +static int moxtet_register_debugfs(struct moxtet *moxtet) +{ + struct dentry *root, *entry; + + root = debugfs_create_dir("moxtet", NULL); + + if (IS_ERR(root)) + return PTR_ERR(root); + + entry = debugfs_create_file_unsafe("input", 0444, root, moxtet, + &input_fops); + if (IS_ERR(entry)) + goto err_remove; + + entry = debugfs_create_file_unsafe("output", 0644, root, moxtet, + &output_fops); + if (IS_ERR(entry)) + goto err_remove; + + moxtet->debugfs_root = root; + + return 0; +err_remove: + debugfs_remove_recursive(root); + return PTR_ERR(entry); +} + +static void moxtet_unregister_debugfs(struct moxtet *moxtet) +{ + debugfs_remove_recursive(moxtet->debugfs_root); +} +#else +static inline int moxtet_register_debugfs(struct moxtet *moxtet) +{ + return 0; +} + +static inline void moxtet_unregister_debugfs(struct moxtet *moxtet) +{ +} +#endif + +static int moxtet_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct moxtet *moxtet = d->host_data; + + if (hw >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(hw))) { + dev_err(moxtet->dev, "Invalid hw irq number\n"); + return -EINVAL; + } + + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &moxtet->irq.chip, handle_level_irq); + + return 0; +} + +static int moxtet_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + struct moxtet *moxtet = d->host_data; + int irq; + + if (WARN_ON(intsize < 1)) + return -EINVAL; + + irq = intspec[0]; + + if (irq >= MOXTET_NIRQS || !(moxtet->irq.exists & BIT(irq))) + return -EINVAL; + + *out_hwirq = irq; + *out_type = IRQ_TYPE_NONE; + return 0; +} + +static const struct irq_domain_ops moxtet_irq_domain = { + .map = moxtet_irq_domain_map, + .xlate = moxtet_irq_domain_xlate, +}; + +static void moxtet_irq_mask(struct irq_data *d) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + + moxtet->irq.masked |= BIT(d->hwirq); +} + +static void moxtet_irq_unmask(struct irq_data *d) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + + moxtet->irq.masked &= ~BIT(d->hwirq); +} + +static void moxtet_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct moxtet *moxtet = irq_data_get_irq_chip_data(d); + struct moxtet_irqpos *pos = &moxtet->irq.position[d->hwirq]; + int id; + + id = moxtet->modules[pos->idx]; + + seq_printf(p, " moxtet-%s.%i#%i", mox_module_name(id), pos->idx, + pos->bit); +} + +static const struct irq_chip moxtet_irq_chip = { + .name = "moxtet", + .irq_mask = moxtet_irq_mask, + .irq_unmask = moxtet_irq_unmask, + .irq_print_chip = moxtet_irq_print_chip, +}; + +static int moxtet_irq_read(struct moxtet *moxtet, unsigned long *map) +{ + struct moxtet_irqpos *pos = moxtet->irq.position; + u8 buf[TURRIS_MOX_MAX_MODULES]; + int i, ret; + + ret = moxtet_spi_read(moxtet, buf); + if (ret < 0) + return ret; + + *map = 0; + + for_each_set_bit(i, &moxtet->irq.exists, MOXTET_NIRQS) { + if (!(buf[pos[i].idx + 1] & BIT(4 + pos[i].bit))) + set_bit(i, map); + } + + return 0; +} + +static irqreturn_t moxtet_irq_thread_fn(int irq, void *data) +{ + struct moxtet *moxtet = data; + unsigned long set; + int nhandled = 0, i, sub_irq, ret; + + ret = moxtet_irq_read(moxtet, &set); + if (ret < 0) + goto out; + + set &= ~moxtet->irq.masked; + + do { + for_each_set_bit(i, &set, MOXTET_NIRQS) { + sub_irq = irq_find_mapping(moxtet->irq.domain, i); + handle_nested_irq(sub_irq); + dev_dbg(moxtet->dev, "%i irq\n", i); + ++nhandled; + } + + ret = moxtet_irq_read(moxtet, &set); + if (ret < 0) + goto out; + + set &= ~moxtet->irq.masked; + } while (set); + +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static void moxtet_irq_free(struct moxtet *moxtet) +{ + int i, irq; + + for (i = 0; i < MOXTET_NIRQS; ++i) { + if (moxtet->irq.exists & BIT(i)) { + irq = irq_find_mapping(moxtet->irq.domain, i); + irq_dispose_mapping(irq); + } + } + + irq_domain_remove(moxtet->irq.domain); +} + +static int moxtet_irq_setup(struct moxtet *moxtet) +{ + int i, ret; + + moxtet->irq.domain = irq_domain_add_simple(moxtet->dev->of_node, + MOXTET_NIRQS, 0, + &moxtet_irq_domain, moxtet); + if (moxtet->irq.domain == NULL) { + dev_err(moxtet->dev, "Could not add IRQ domain\n"); + return -ENOMEM; + } + + for (i = 0; i < MOXTET_NIRQS; ++i) + if (moxtet->irq.exists & BIT(i)) + irq_create_mapping(moxtet->irq.domain, i); + + moxtet->irq.chip = moxtet_irq_chip; + moxtet->irq.masked = ~0; + + ret = request_threaded_irq(moxtet->dev_irq, NULL, moxtet_irq_thread_fn, + IRQF_ONESHOT, "moxtet", moxtet); + if (ret < 0) + goto err_free; + + return 0; + +err_free: + moxtet_irq_free(moxtet); + return ret; +} + +static int moxtet_probe(struct spi_device *spi) +{ + struct moxtet *moxtet; + int ret; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + moxtet = devm_kzalloc(&spi->dev, sizeof(struct moxtet), + GFP_KERNEL); + if (!moxtet) + return -ENOMEM; + + moxtet->dev = &spi->dev; + spi_set_drvdata(spi, moxtet); + + mutex_init(&moxtet->lock); + + moxtet->dev_irq = of_irq_get(moxtet->dev->of_node, 0); + if (moxtet->dev_irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (moxtet->dev_irq <= 0) { + dev_err(moxtet->dev, "No IRQ resource found\n"); + return -ENXIO; + } + + ret = moxtet_find_topology(moxtet); + if (ret < 0) + return ret; + + if (moxtet->irq.exists) { + ret = moxtet_irq_setup(moxtet); + if (ret < 0) + return ret; + } + + of_register_moxtet_devices(moxtet); + moxtet_register_devices_from_topology(moxtet); + + ret = moxtet_register_debugfs(moxtet); + if (ret < 0) + dev_warn(moxtet->dev, "Failed creating debugfs entries: %i\n", + ret); + + return 0; +} + +static int moxtet_remove(struct spi_device *spi) +{ + struct moxtet *moxtet = spi_get_drvdata(spi); + int dummy; + + free_irq(moxtet->dev_irq, moxtet); + + moxtet_irq_free(moxtet); + + moxtet_unregister_debugfs(moxtet); + + dummy = device_for_each_child(moxtet->dev, NULL, __unregister); + + mutex_destroy(&moxtet->lock); + + return 0; +} + +static const struct of_device_id moxtet_dt_ids[] = { + { .compatible = "cznic,moxtet" }, + {}, +}; +MODULE_DEVICE_TABLE(of, moxtet_dt_ids); + +static struct spi_driver moxtet_spi_driver = { + .driver = { + .name = "moxtet", + .of_match_table = moxtet_dt_ids, + }, + .probe = moxtet_probe, + .remove = moxtet_remove, +}; + +static int __init moxtet_init(void) +{ + int ret; + + ret = bus_register(&moxtet_bus_type); + if (ret < 0) { + pr_err("moxtet bus registration failed: %d\n", ret); + goto error; + } + + ret = spi_register_driver(&moxtet_spi_driver); + if (ret < 0) { + pr_err("moxtet spi driver registration failed: %d\n", ret); + goto error_bus; + } + + return 0; + +error_bus: + bus_unregister(&moxtet_bus_type); +error: + return ret; +} +postcore_initcall_sync(moxtet_init); + +static void __exit moxtet_exit(void) +{ + spi_unregister_driver(&moxtet_spi_driver); + bus_unregister(&moxtet_bus_type); +} +module_exit(moxtet_exit); + +MODULE_AUTHOR("Marek Behun "); +MODULE_DESCRIPTION("CZ.NIC's Turris Mox module configuration bus"); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/bus/moxtet.h b/include/dt-bindings/bus/moxtet.h new file mode 100644 index 000000000000..dc9345440ebe --- /dev/null +++ b/include/dt-bindings/bus/moxtet.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Constant for device tree bindings for Turris Mox module configuration bus + * + * Copyright (C) 2019 Marek Behun + */ + +#ifndef _DT_BINDINGS_BUS_MOXTET_H +#define _DT_BINDINGS_BUS_MOXTET_H + +#define MOXTET_IRQ_PCI 0 +#define MOXTET_IRQ_USB3 4 +#define MOXTET_IRQ_PERIDOT(n) (8 + (n)) +#define MOXTET_IRQ_TOPAZ 12 + +#endif /* _DT_BINDINGS_BUS_MOXTET_H */ diff --git a/include/linux/moxtet.h b/include/linux/moxtet.h new file mode 100644 index 000000000000..490db6886dcc --- /dev/null +++ b/include/linux/moxtet.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Turris Mox module configuration bus driver + * + * Copyright (C) 2019 Marek Behun + */ + +#ifndef __LINUX_MOXTET_H +#define __LINUX_MOXTET_H + +#include +#include +#include +#include + +#define TURRIS_MOX_MAX_MODULES 10 + +enum turris_mox_cpu_module_id { + TURRIS_MOX_CPU_ID_EMMC = 0x00, + TURRIS_MOX_CPU_ID_SD = 0x10, +}; + +enum turris_mox_module_id { + TURRIS_MOX_MODULE_FIRST = 0x01, + + TURRIS_MOX_MODULE_SFP = 0x01, + TURRIS_MOX_MODULE_PCI = 0x02, + TURRIS_MOX_MODULE_TOPAZ = 0x03, + TURRIS_MOX_MODULE_PERIDOT = 0x04, + TURRIS_MOX_MODULE_USB3 = 0x05, + TURRIS_MOX_MODULE_PCI_BRIDGE = 0x06, + + TURRIS_MOX_MODULE_LAST = 0x06, +}; + +#define MOXTET_NIRQS 16 + +extern struct bus_type moxtet_type; + +struct moxtet { + struct device *dev; + struct mutex lock; + u8 modules[TURRIS_MOX_MAX_MODULES]; + int count; + u8 tx[TURRIS_MOX_MAX_MODULES]; + int dev_irq; + struct { + struct irq_domain *domain; + struct irq_chip chip; + unsigned long masked, exists; + struct moxtet_irqpos { + u8 idx; + u8 bit; + } position[MOXTET_NIRQS]; + } irq; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; +#endif +}; + +struct moxtet_driver { + const enum turris_mox_module_id *id_table; + struct device_driver driver; +}; + +static inline struct moxtet_driver * +to_moxtet_driver(struct device_driver *drv) +{ + if (!drv) + return NULL; + return container_of(drv, struct moxtet_driver, driver); +} + +extern int __moxtet_register_driver(struct module *owner, + struct moxtet_driver *mdrv); + +static inline void moxtet_unregister_driver(struct moxtet_driver *mdrv) +{ + if (mdrv) + driver_unregister(&mdrv->driver); +} + +#define moxtet_register_driver(driver) \ + __moxtet_register_driver(THIS_MODULE, driver) + +#define module_moxtet_driver(__moxtet_driver) \ + module_driver(__moxtet_driver, moxtet_register_driver, \ + moxtet_unregister_driver) + +struct moxtet_device { + struct device dev; + struct moxtet *moxtet; + enum turris_mox_module_id id; + unsigned int idx; +}; + +extern int moxtet_device_read(struct device *dev); +extern int moxtet_device_write(struct device *dev, u8 val); +extern int moxtet_device_written(struct device *dev); + +static inline struct moxtet_device * +to_moxtet_device(struct device *dev) +{ + if (!dev) + return NULL; + return container_of(dev, struct moxtet_device, dev); +} + +#endif /* __LINUX_MOXTET_H */ From 20e6cf7e747815461ac8bcb526f7079ce48866b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 12 Aug 2019 18:11:15 +0200 Subject: [PATCH 062/123] dt-bindings: bus: Document moxtet bus binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds device tree binding documentation for the Moxtet bus, a bus via which the different modules connected to the Turris Mox router can be configured. Link: https://lore.kernel.org/r/20190812161118.21476-3-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Rob Herring Signed-off-by: Arnd Bergmann --- .../devicetree/bindings/bus/moxtet.txt | 46 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/moxtet.txt diff --git a/Documentation/devicetree/bindings/bus/moxtet.txt b/Documentation/devicetree/bindings/bus/moxtet.txt new file mode 100644 index 000000000000..fb50fc865336 --- /dev/null +++ b/Documentation/devicetree/bindings/bus/moxtet.txt @@ -0,0 +1,46 @@ +Turris Mox module status and configuration bus (over SPI) + +Required properties: + - compatible : Should be "cznic,moxtet" + - #address-cells : Has to be 1 + - #size-cells : Has to be 0 + - spi-cpol : Required inverted clock polarity + - spi-cpha : Required shifted clock phase + - interrupts : Must contain reference to the shared interrupt line + - interrupt-controller : Required + - #interrupt-cells : Has to be 1 + +For other required and optional properties of SPI slave nodes please refer to +../spi/spi-bus.txt. + +Required properties of subnodes: + - reg : Should be position on the Moxtet bus (how many Moxtet + modules are between this module and CPU module, so + either 0 or a positive integer) + +The driver finds the devices connected to the bus by itself, but it may be +needed to reference some of them from other parts of the device tree. In that +case the devices can be defined as subnodes of the moxtet node. + +Example: + + moxtet@1 { + compatible = "cznic,moxtet"; + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + spi-max-frequency = <10000000>; + spi-cpol; + spi-cpha; + interrupt-controller; + #interrupt-cells = <1>; + interrupt-parent = <&gpiosb>; + interrupts = <5 IRQ_TYPE_EDGE_FALLING>; + + moxtet_sfp: gpio@0 { + compatible = "cznic,moxtet-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0>; + } + }; diff --git a/MAINTAINERS b/MAINTAINERS index 01309a228593..e0b803ba1418 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1630,6 +1630,7 @@ ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun W: http://mox.turris.cz S: Maintained +F: Documentation/devicetree/bindings/bus/moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c From 2091b417aab195a2c35abe780ad15ade1b0932ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 12 Aug 2019 18:11:16 +0200 Subject: [PATCH 063/123] bus: moxtet: Add sysfs and debugfs documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sysfs ABI documentation for the attribute files module_id and module_name Add debugfs ABI documentation for reading input from the shift registers and reading last written output or write output to the shift registers. Link: https://lore.kernel.org/r/20190812161118.21476-4-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- Documentation/ABI/testing/debugfs-moxtet | 23 +++++++++++++++++++ .../ABI/testing/sysfs-bus-moxtet-devices | 17 ++++++++++++++ MAINTAINERS | 2 ++ 3 files changed, 42 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-moxtet create mode 100644 Documentation/ABI/testing/sysfs-bus-moxtet-devices diff --git a/Documentation/ABI/testing/debugfs-moxtet b/Documentation/ABI/testing/debugfs-moxtet new file mode 100644 index 000000000000..67b1717794d8 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-moxtet @@ -0,0 +1,23 @@ +What: /sys/kernel/debug/moxtet/input +Date: March 2019 +KernelVersion: 5.3 +Contact: Marek Behún +Description: (R) Read input from the shift registers, in hexadecimal. + Returns N+1 bytes, where N is the number of Moxtet connected + modules. The first byte is from the CPU board itself. + Example: 101214 + 10: CPU board with SD card + 12: 2 = PCIe module, 1 = IRQ not active + 14: 4 = Peridot module, 1 = IRQ not active + +What: /sys/kernel/debug/moxtet/output +Date: March 2019 +KernelVersion: 5.3 +Contact: Marek Behún +Description: (RW) Read last written value to the shift registers, in + hexadecimal, or write values to the shift registers, also + in hexadecimal. + Example: 0102 + 01: 01 was last written, or is to be written, to the + first module's shift register + 02: the same for second module diff --git a/Documentation/ABI/testing/sysfs-bus-moxtet-devices b/Documentation/ABI/testing/sysfs-bus-moxtet-devices new file mode 100644 index 000000000000..355958527fa3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-moxtet-devices @@ -0,0 +1,17 @@ +What: /sys/bus/moxtet/devices/moxtet-./module_description +Date: March 2019 +KernelVersion: 5.3 +Contact: Marek Behún +Description: (R) Moxtet module description. Format: string + +What: /sys/bus/moxtet/devices/moxtet-./module_id +Date: March 2019 +KernelVersion: 5.3 +Contact: Marek Behún +Description: (R) Moxtet module ID. Format: %x + +What: /sys/bus/moxtet/devices/moxtet-./module_name +Date: March 2019 +KernelVersion: 5.3 +Contact: Marek Behún +Description: (R) Moxtet module name. Format: string diff --git a/MAINTAINERS b/MAINTAINERS index e0b803ba1418..1869e83f514a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1630,6 +1630,8 @@ ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun W: http://mox.turris.cz S: Maintained +F: Documentation/ABI/testing/debugfs-moxtet +F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/devicetree/bindings/bus/moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c From 856ed97af5cfcfdc73cc4178d9670122bca52f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 12 Aug 2019 18:11:17 +0200 Subject: [PATCH 064/123] drivers: gpio: Add support for GPIOs over Moxtet bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for interpreting the input and output bits of one device on Moxtet bus as GPIOs. This is needed for example by the SFP cage module of Turris Mox. Link: https://lore.kernel.org/r/20190812161118.21476-5-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-moxtet.c | 179 +++++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 drivers/gpio/gpio-moxtet.c diff --git a/MAINTAINERS b/MAINTAINERS index 1869e83f514a..a71ab252ad7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1635,6 +1635,7 @@ F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/devicetree/bindings/bus/moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c +F: drivers/gpio/gpio-moxtet.c ARM/EBSA110 MACHINE SUPPORT M: Russell King diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bb13c266c329..eef4886a3bc3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1445,6 +1445,15 @@ config GPIO_XRA1403 help GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander. +config GPIO_MOXTET + tristate "Turris Mox Moxtet bus GPIO expander" + depends on MOXTET + help + Say yes here if you are building for the Turris Mox router. + This is the driver needed for configuring the GPIOs via the Moxtet + bus. For example the Mox module with SFP cage needs this driver + so that phylink can use corresponding GPIOs. + endmenu menu "USB GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a4e91175c708..2e10caec23ae 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -93,6 +93,7 @@ obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o +obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c new file mode 100644 index 000000000000..3fd729994a38 --- /dev/null +++ b/drivers/gpio/gpio-moxtet.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox Moxtet GPIO expander + * + * Copyright (C) 2018 Marek Behun + */ + +#include +#include +#include +#include + +#define MOXTET_GPIO_NGPIOS 12 +#define MOXTET_GPIO_INPUTS 4 + +struct moxtet_gpio_desc { + u16 in_mask; + u16 out_mask; +}; + +static const struct moxtet_gpio_desc descs[] = { + [TURRIS_MOX_MODULE_SFP] = { + .in_mask = GENMASK(2, 0), + .out_mask = GENMASK(5, 4), + }, +}; + +struct moxtet_gpio_chip { + struct device *dev; + struct gpio_chip gpio_chip; + const struct moxtet_gpio_desc *desc; +}; + +static int moxtet_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + int ret; + + if (chip->desc->in_mask & BIT(offset)) { + ret = moxtet_device_read(chip->dev); + } else if (chip->desc->out_mask & BIT(offset)) { + ret = moxtet_device_written(chip->dev); + if (ret >= 0) + ret <<= MOXTET_GPIO_INPUTS; + } else { + return -EINVAL; + } + + if (ret < 0) + return ret; + + return !!(ret & BIT(offset)); +} + +static void moxtet_gpio_set_value(struct gpio_chip *gc, unsigned int offset, + int val) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + int state; + + state = moxtet_device_written(chip->dev); + if (state < 0) + return; + + offset -= MOXTET_GPIO_INPUTS; + + if (val) + state |= BIT(offset); + else + state &= ~BIT(offset); + + moxtet_device_write(chip->dev, state); +} + +static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + /* All lines are hard wired to be either input or output, not both. */ + if (chip->desc->in_mask & BIT(offset)) + return 1; + else if (chip->desc->out_mask & BIT(offset)) + return 0; + else + return -EINVAL; +} + +static int moxtet_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + if (chip->desc->in_mask & BIT(offset)) + return 0; + else if (chip->desc->out_mask & BIT(offset)) + return -ENOTSUPP; + else + return -EINVAL; +} + +static int moxtet_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int val) +{ + struct moxtet_gpio_chip *chip = gpiochip_get_data(gc); + + if (chip->desc->out_mask & BIT(offset)) + moxtet_gpio_set_value(gc, offset, val); + else if (chip->desc->in_mask & BIT(offset)) + return -ENOTSUPP; + else + return -EINVAL; + + return 0; +} + +static int moxtet_gpio_probe(struct device *dev) +{ + struct moxtet_gpio_chip *chip; + struct device_node *nc = dev->of_node; + int id; + + id = to_moxtet_device(dev)->id; + + if (id >= ARRAY_SIZE(descs)) { + dev_err(dev, "%pOF Moxtet device id 0x%x is not supported by gpio-moxtet driver\n", + nc, id); + return -ENOTSUPP; + } + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + chip->gpio_chip.parent = dev; + chip->desc = &descs[id]; + + dev_set_drvdata(dev, chip); + + chip->gpio_chip.label = dev_name(dev); + chip->gpio_chip.get_direction = moxtet_gpio_get_direction; + chip->gpio_chip.direction_input = moxtet_gpio_direction_input; + chip->gpio_chip.direction_output = moxtet_gpio_direction_output; + chip->gpio_chip.get = moxtet_gpio_get_value; + chip->gpio_chip.set = moxtet_gpio_set_value; + chip->gpio_chip.base = -1; + + chip->gpio_chip.ngpio = MOXTET_GPIO_NGPIOS; + + chip->gpio_chip.can_sleep = true; + chip->gpio_chip.owner = THIS_MODULE; + + return devm_gpiochip_add_data(dev, &chip->gpio_chip, chip); +} + +static const struct of_device_id moxtet_gpio_dt_ids[] = { + { .compatible = "cznic,moxtet-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, moxtet_gpio_dt_ids); + +static const enum turris_mox_module_id moxtet_gpio_module_table[] = { + TURRIS_MOX_MODULE_SFP, + 0, +}; + +static struct moxtet_driver moxtet_gpio_driver = { + .driver = { + .name = "moxtet-gpio", + .of_match_table = moxtet_gpio_dt_ids, + .probe = moxtet_gpio_probe, + }, + .id_table = moxtet_gpio_module_table, +}; +module_moxtet_driver(moxtet_gpio_driver); + +MODULE_AUTHOR("Marek Behun "); +MODULE_DESCRIPTION("Turris Mox Moxtet GPIO expander"); +MODULE_LICENSE("GPL v2"); From 8c9e465b488b2b8b83b321045f4c07adbd69d2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 12 Aug 2019 18:11:18 +0200 Subject: [PATCH 065/123] dt-bindings: gpio: Document GPIOs via Moxtet bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds documentation of the device tree bindings for GPIOs on the devices connected via Moxtet bus. Link: https://lore.kernel.org/r/20190812161118.21476-6-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Arnd Bergmann --- .../devicetree/bindings/gpio/gpio-moxtet.txt | 18 ++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-moxtet.txt diff --git a/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt b/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt new file mode 100644 index 000000000000..410759de9f09 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-moxtet.txt @@ -0,0 +1,18 @@ +Turris Mox Moxtet GPIO expander via Moxtet bus + +Required properties: + - compatible : Should be "cznic,moxtet-gpio". + - gpio-controller : Marks the device node as a GPIO controller. + - #gpio-cells : Should be two. For consumer use see gpio.txt. + +Other properties are required for a Moxtet bus device, please refer to +Documentation/devicetree/bindings/bus/moxtet.txt. + +Example: + + moxtet_sfp: gpio@0 { + compatible = "cznic,moxtet-gpio"; + gpio-controller; + #gpio-cells = <2>; + reg = <0>; + } diff --git a/MAINTAINERS b/MAINTAINERS index a71ab252ad7e..0872e4cb98ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1633,6 +1633,7 @@ S: Maintained F: Documentation/ABI/testing/debugfs-moxtet F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/devicetree/bindings/bus/moxtet.txt +F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c F: drivers/gpio/gpio-moxtet.c From 3253b7b7cd44c4dd029a4ce280ef9f409a256e5f Mon Sep 17 00:00:00 2001 From: Pankaj Dubey Date: Tue, 13 Aug 2019 17:08:19 +0200 Subject: [PATCH 066/123] soc: samsung: Add exynos chipid driver support Exynos SoCs have Chipid, for identification of product IDs and SoC revisions. This patch intends to provide initialization code for all these functionalities, at the same time it provides some sysfs entries for accessing these information to user-space. This driver uses existing binding for exynos-chipid. Changes by Bartlomiej: - fixed return values on errors - removed bogus kfree_const() - added missing Exynos4210 EVT0 id - converted code to use EXYNOS_MASK define - fixed np use after of_node_put() - fixed too early use of dev_info() - made driver fail for unknown SoC-s - added SPDX tag - updated Copyrights Signed-off-by: Pankaj Dubey [m.szyprowski: for suggestion and code snippet of product_id_to_soc_id] Signed-off-by: Marek Szyprowski Signed-off-by: Bartlomiej Zolnierkiewicz [s.nawrocki: updated copyright date, removed uneeded headers inclusion] Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/Kconfig | 5 ++ drivers/soc/samsung/Makefile | 2 + drivers/soc/samsung/exynos-chipid.c | 109 ++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 drivers/soc/samsung/exynos-chipid.c diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 2186285fda92..2905f5262197 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -7,6 +7,11 @@ menuconfig SOC_SAMSUNG if SOC_SAMSUNG +config EXYNOS_CHIPID + bool "Exynos Chipid controller driver" if COMPILE_TEST + depends on ARCH_EXYNOS || COMPILE_TEST + select SOC_BUS + config EXYNOS_PMU bool "Exynos PMU controller driver" if COMPILE_TEST depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 29f294baac6e..3b6a8797416c 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c new file mode 100644 index 000000000000..bcf691f2b650 --- /dev/null +++ b/drivers/soc/samsung/exynos-chipid.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - CHIP ID support + * Author: Pankaj Dubey + * Author: Bartlomiej Zolnierkiewicz + */ + +#include +#include +#include +#include +#include + +#define EXYNOS_SUBREV_MASK (0xF << 4) +#define EXYNOS_MAINREV_MASK (0xF << 0) +#define EXYNOS_REV_MASK (EXYNOS_SUBREV_MASK | EXYNOS_MAINREV_MASK) +#define EXYNOS_MASK 0xFFFFF000 + +static const struct exynos_soc_id { + const char *name; + unsigned int id; +} soc_ids[] = { + { "EXYNOS3250", 0xE3472000 }, + { "EXYNOS4210", 0x43200000 }, /* EVT0 revision */ + { "EXYNOS4210", 0x43210000 }, + { "EXYNOS4212", 0x43220000 }, + { "EXYNOS4412", 0xE4412000 }, + { "EXYNOS5250", 0x43520000 }, + { "EXYNOS5260", 0xE5260000 }, + { "EXYNOS5410", 0xE5410000 }, + { "EXYNOS5420", 0xE5420000 }, + { "EXYNOS5440", 0xE5440000 }, + { "EXYNOS5800", 0xE5422000 }, + { "EXYNOS7420", 0xE7420000 }, + { "EXYNOS5433", 0xE5433000 }, +}; + +static const char * __init product_id_to_soc_id(unsigned int product_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(soc_ids); i++) + if ((product_id & EXYNOS_MASK) == soc_ids[i].id) + return soc_ids[i].name; + return NULL; +} + +int __init exynos_chipid_early_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + void __iomem *exynos_chipid_base; + struct soc_device *soc_dev; + struct device_node *root; + struct device_node *np; + u32 product_id; + u32 revision; + + /* look up for chipid node */ + np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); + if (!np) + return -ENODEV; + + exynos_chipid_base = of_iomap(np, 0); + of_node_put(np); + + if (!exynos_chipid_base) { + pr_err("Failed to map SoC chipid\n"); + return -ENXIO; + } + + product_id = readl_relaxed(exynos_chipid_base); + revision = product_id & EXYNOS_REV_MASK; + iounmap(exynos_chipid_base); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->family = "Samsung Exynos"; + + root = of_find_node_by_path("/"); + of_property_read_string(root, "model", &soc_dev_attr->machine); + of_node_put(root); + + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision); + soc_dev_attr->soc_id = product_id_to_soc_id(product_id); + if (!soc_dev_attr->soc_id) { + pr_err("Unknown SoC\n"); + return -ENODEV; + } + + /* please note that the actual registration will be deferred */ + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return PTR_ERR(soc_dev); + } + + /* it is too early to use dev_info() here (soc_dev is NULL) */ + pr_info("soc soc0: Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", + soc_dev_attr->soc_id, product_id, revision); + + return 0; +} +early_initcall(exynos_chipid_early_init); From 40d8aff614f71ab3cab20785b4f213e3802d4e87 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 13 Aug 2019 17:08:20 +0200 Subject: [PATCH 067/123] soc: samsung: chipid: Convert exynos-chipid driver to use the regmap API Convert the driver to use regmap API in order to allow other drivers, like ASV, to access the CHIPID registers. Add definition of selected CHIPID register offsets and register bit fields for Exynos5422 SoC. Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 34 ++++++--------- include/linux/soc/samsung/exynos-chipid.h | 52 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 include/linux/soc/samsung/exynos-chipid.h diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index bcf691f2b650..006a95feb618 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -9,16 +9,13 @@ */ #include +#include #include -#include +#include #include +#include #include -#define EXYNOS_SUBREV_MASK (0xF << 4) -#define EXYNOS_MAINREV_MASK (0xF << 0) -#define EXYNOS_REV_MASK (EXYNOS_SUBREV_MASK | EXYNOS_MAINREV_MASK) -#define EXYNOS_MASK 0xFFFFF000 - static const struct exynos_soc_id { const char *name; unsigned int id; @@ -51,29 +48,24 @@ static const char * __init product_id_to_soc_id(unsigned int product_id) int __init exynos_chipid_early_init(void) { struct soc_device_attribute *soc_dev_attr; - void __iomem *exynos_chipid_base; struct soc_device *soc_dev; struct device_node *root; - struct device_node *np; + struct regmap *regmap; u32 product_id; u32 revision; + int ret; - /* look up for chipid node */ - np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-chipid"); - if (!np) - return -ENODEV; - - exynos_chipid_base = of_iomap(np, 0); - of_node_put(np); - - if (!exynos_chipid_base) { - pr_err("Failed to map SoC chipid\n"); - return -ENXIO; + regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid"); + if (IS_ERR(regmap)) { + pr_err("Failed to get CHIPID regmap\n"); + return PTR_ERR(regmap); } - product_id = readl_relaxed(exynos_chipid_base); + ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); + if (ret < 0) + return ret; + revision = product_id & EXYNOS_REV_MASK; - iounmap(exynos_chipid_base); soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); if (!soc_dev_attr) diff --git a/include/linux/soc/samsung/exynos-chipid.h b/include/linux/soc/samsung/exynos-chipid.h new file mode 100644 index 000000000000..8bca6763f99c --- /dev/null +++ b/include/linux/soc/samsung/exynos-chipid.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Exynos - CHIPID support + */ +#ifndef __LINUX_SOC_EXYNOS_CHIPID_H +#define __LINUX_SOC_EXYNOS_CHIPID_H + +#define EXYNOS_CHIPID_REG_PRO_ID 0x00 +#define EXYNOS_SUBREV_MASK (0xf << 4) +#define EXYNOS_MAINREV_MASK (0xf << 0) +#define EXYNOS_REV_MASK (EXYNOS_SUBREV_MASK | \ + EXYNOS_MAINREV_MASK) +#define EXYNOS_MASK 0xfffff000 + +#define EXYNOS_CHIPID_REG_PKG_ID 0x04 +/* Bit field definitions for EXYNOS_CHIPID_REG_PKG_ID register */ +#define EXYNOS5422_IDS_OFFSET 24 +#define EXYNOS5422_IDS_MASK 0xff +#define EXYNOS5422_USESG_OFFSET 3 +#define EXYNOS5422_USESG_MASK 0x01 +#define EXYNOS5422_SG_OFFSET 0 +#define EXYNOS5422_SG_MASK 0x07 +#define EXYNOS5422_TABLE_OFFSET 8 +#define EXYNOS5422_TABLE_MASK 0x03 +#define EXYNOS5422_SG_A_OFFSET 17 +#define EXYNOS5422_SG_A_MASK 0x0f +#define EXYNOS5422_SG_B_OFFSET 21 +#define EXYNOS5422_SG_B_MASK 0x03 +#define EXYNOS5422_SG_BSIGN_OFFSET 23 +#define EXYNOS5422_SG_BSIGN_MASK 0x01 +#define EXYNOS5422_BIN2_OFFSET 12 +#define EXYNOS5422_BIN2_MASK 0x01 + +#define EXYNOS_CHIPID_REG_LOT_ID 0x14 + +#define EXYNOS_CHIPID_REG_AUX_INFO 0x1c +/* Bit field definitions for EXYNOS_CHIPID_REG_AUX_INFO register */ +#define EXYNOS5422_TMCB_OFFSET 0 +#define EXYNOS5422_TMCB_MASK 0x7f +#define EXYNOS5422_ARM_UP_OFFSET 8 +#define EXYNOS5422_ARM_UP_MASK 0x03 +#define EXYNOS5422_ARM_DN_OFFSET 10 +#define EXYNOS5422_ARM_DN_MASK 0x03 +#define EXYNOS5422_KFC_UP_OFFSET 12 +#define EXYNOS5422_KFC_UP_MASK 0x03 +#define EXYNOS5422_KFC_DN_OFFSET 14 +#define EXYNOS5422_KFC_DN_MASK 0x03 + +#endif /*__LINUX_SOC_EXYNOS_CHIPID_H */ From 031bca078e7554f1aa571a000272a52a07b1735f Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 30 Jul 2019 14:36:06 -0700 Subject: [PATCH 068/123] soc: fsl: qbman: Remove dev_err() usage after platform_get_irq() We don't need dev_err() messages when platform_get_irq() fails now that platform_get_irq() prints an error message itself when something goes wrong. Let's remove these prints with a simple semantic patch. // @@ expression ret; struct platform_device *E; @@ ret = ( platform_get_irq(E, ...) | platform_get_irq_byname(E, ...) ); if ( \( ret < 0 \| ret <= 0 \) ) { ( -if (ret != -EPROBE_DEFER) -{ ... -dev_err(...); -... } | ... -dev_err(...); ) ... } // While we're here, remove braces on if statements that only have one statement (manually). Cc: Thierry Reding Cc: Li Yang Cc: Simon Horman Cc: Greg Kroah-Hartman Signed-off-by: Stephen Boyd Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman_portal.c | 4 +--- drivers/soc/fsl/qbman/qman_portal.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c index cf4f10d6f590..e4ef35abb508 100644 --- a/drivers/soc/fsl/qbman/bman_portal.c +++ b/drivers/soc/fsl/qbman/bman_portal.c @@ -135,10 +135,8 @@ static int bman_portal_probe(struct platform_device *pdev) pcfg->cpu = -1; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "Can't get %pOF IRQ'\n", node); + if (irq <= 0) goto err_ioremap1; - } pcfg->irq = irq; pcfg->addr_virt_ce = memremap(addr_phys[0]->start, diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c index e2186b681d87..991c35a72e00 100644 --- a/drivers/soc/fsl/qbman/qman_portal.c +++ b/drivers/soc/fsl/qbman/qman_portal.c @@ -275,10 +275,8 @@ static int qman_portal_probe(struct platform_device *pdev) pcfg->channel = val; pcfg->cpu = -1; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "Can't get %pOF IRQ\n", node); + if (irq <= 0) goto err_ioremap1; - } pcfg->irq = irq; pcfg->addr_virt_ce = memremap(addr_phys[0]->start, From 97777078d695d4294c999ad29363ff11f6384442 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:16:53 +0000 Subject: [PATCH 069/123] soc/fsl/qbman: Rework QBMan private memory setup Rework QBMan private memory setup so that the areas are not zeroed if the device was previously initialized If the QMan private memory was already initialized skip the PFDR initialization. Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman_ccsr.c | 26 ++++++++++++-- drivers/soc/fsl/qbman/dpaa_sys.c | 7 ++-- drivers/soc/fsl/qbman/qman_ccsr.c | 56 +++++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index 7c3cc968053c..dc6d7e56be27 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -97,17 +97,40 @@ static void bm_get_version(u16 *id, u8 *major, u8 *minor) /* signal transactions for FBPRs with higher priority */ #define FBPR_AR_RPRIO_HI BIT(30) -static void bm_set_memory(u64 ba, u32 size) +/* Track if probe has occurred and if cleanup is required */ +static int __bman_probed; +static int __bman_requires_cleanup; + + +static int bm_set_memory(u64 ba, u32 size) { + u32 bar, bare; u32 exp = ilog2(size); /* choke if size isn't within range */ DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 && is_power_of_2(size)); /* choke if '[e]ba' has lower-alignment than 'size' */ DPAA_ASSERT(!(ba & (size - 1))); + + /* Check to see if BMan has already been initialized */ + bar = bm_ccsr_in(REG_FBPR_BAR); + if (bar) { + /* Maker sure ba == what was programmed) */ + bare = bm_ccsr_in(REG_FBPR_BARE); + if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) { + pr_err("Attempted to reinitialize BMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n", + ba, bare, bar); + return -ENOMEM; + } + pr_info("BMan BAR already configured\n"); + __bman_requires_cleanup = 1; + return 1; + } + bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba)); bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba)); bm_ccsr_out(REG_FBPR_AR, exp - 1); + return 0; } /* @@ -120,7 +143,6 @@ static void bm_set_memory(u64 ba, u32 size) */ static dma_addr_t fbpr_a; static size_t fbpr_sz; -static int __bman_probed; static int bman_fbpr(struct reserved_mem *rmem) { diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c index e6d48dccb8d5..3e0a7f311693 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.c +++ b/drivers/soc/fsl/qbman/dpaa_sys.c @@ -40,6 +40,7 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, int ret; struct device_node *mem_node; u64 size64; + struct reserved_mem *rmem; ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx); if (ret) { @@ -62,10 +63,8 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, return -ENODEV; } - if (!dma_alloc_coherent(dev, *size, addr, 0)) { - dev_err(dev, "DMA Alloc memory failed\n"); - return -ENODEV; - } + rmem = of_reserved_mem_lookup(mem_node); + *addr = rmem->base; /* * Disassociate the reserved memory area from the device diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index a6bb43007d03..a3edefa792f8 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -274,6 +274,7 @@ static u32 __iomem *qm_ccsr_start; /* A SDQCR mask comprising all the available/visible pool channels */ static u32 qm_pools_sdqcr; static int __qman_probed; +static int __qman_requires_cleanup; static inline u32 qm_ccsr_in(u32 offset) { @@ -340,19 +341,55 @@ static void qm_get_version(u16 *id, u8 *major, u8 *minor) } #define PFDR_AR_EN BIT(31) -static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size) +static int qm_set_memory(enum qm_memory memory, u64 ba, u32 size) { + void *ptr; u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE; u32 exp = ilog2(size); + u32 bar, bare; /* choke if size isn't within range */ DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) && is_power_of_2(size)); /* choke if 'ba' has lower-alignment than 'size' */ DPAA_ASSERT(!(ba & (size - 1))); + + /* Check to see if QMan has already been initialized */ + bar = qm_ccsr_in(offset + REG_offset_BAR); + if (bar) { + /* Maker sure ba == what was programmed) */ + bare = qm_ccsr_in(offset); + if (bare != upper_32_bits(ba) || bar != lower_32_bits(ba)) { + pr_err("Attempted to reinitialize QMan with different BAR, got 0x%llx read BARE=0x%x BAR=0x%x\n", + ba, bare, bar); + return -ENOMEM; + } + __qman_requires_cleanup = 1; + /* Return 1 to indicate memory was previously programmed */ + return 1; + } + /* Need to temporarily map the area to make sure it is zeroed */ + ptr = memremap(ba, size, MEMREMAP_WB); + if (!ptr) { + pr_crit("memremap() of QMan private memory failed\n"); + return -ENOMEM; + } + memset(ptr, 0, size); + +#ifdef CONFIG_PPC + /* + * PPC doesn't appear to flush the cache on memunmap() but the + * cache must be flushed since QMan does non coherent accesses + * to this memory + */ + flush_dcache_range((unsigned long) ptr, (unsigned long) ptr+size); +#endif + memunmap(ptr); + qm_ccsr_out(offset, upper_32_bits(ba)); qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba)); qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1)); + return 0; } static void qm_set_pfdr_threshold(u32 th, u8 k) @@ -571,12 +608,19 @@ static int qman_init_ccsr(struct device *dev) int i, err; /* FQD memory */ - qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz); - /* PFDR memory */ - qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz); - err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8); - if (err) + err = qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz); + if (err < 0) return err; + /* PFDR memory */ + err = qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz); + if (err < 0) + return err; + /* Only initialize PFDRs if the QMan was not initialized before */ + if (err == 0) { + err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8); + if (err) + return err; + } /* thresholds */ qm_set_pfdr_threshold(512, 64); qm_set_sfdr_threshold(128); From 0505d00c8dbaf06cb71e7a16818313ce1959e6d7 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:16:55 +0000 Subject: [PATCH 070/123] soc/fsl/qbman: Cleanup buffer pools if BMan was initialized prior to bootup Clean the BMan buffer pools if the device had been initialized previously. This will ensure a consistent state if the kernel was soft restarted (kexec for example) Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/bman.c | 17 +++++++++-------- drivers/soc/fsl/qbman/bman_ccsr.c | 10 ++++++++++ drivers/soc/fsl/qbman/bman_portal.c | 18 +++++++++++++++++- drivers/soc/fsl/qbman/bman_priv.h | 5 +++++ 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c index f84ab596bde8..f4fb527d8301 100644 --- a/drivers/soc/fsl/qbman/bman.c +++ b/drivers/soc/fsl/qbman/bman.c @@ -635,30 +635,31 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits) return 0; } -static int bm_shutdown_pool(u32 bpid) +int bm_shutdown_pool(u32 bpid) { + int err = 0; struct bm_mc_command *bm_cmd; union bm_mc_result *bm_res; + + struct bman_portal *p = get_affine_portal(); while (1) { - struct bman_portal *p = get_affine_portal(); /* Acquire buffers until empty */ bm_cmd = bm_mc_start(&p->p); bm_cmd->bpid = bpid; bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1); if (!bm_mc_result_timeout(&p->p, &bm_res)) { - put_affine_portal(); pr_crit("BMan Acquire Command timedout\n"); - return -ETIMEDOUT; + err = -ETIMEDOUT; + goto done; } if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) { - put_affine_portal(); /* Pool is empty */ - return 0; + goto done; } - put_affine_portal(); } - +done: + put_affine_portal(); return 0; } diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index dc6d7e56be27..cb24a08be084 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -195,6 +195,16 @@ int bman_is_probed(void) } EXPORT_SYMBOL_GPL(bman_is_probed); +int bman_requires_cleanup(void) +{ + return __bman_requires_cleanup; +} + +void bman_done_cleanup(void) +{ + __bman_requires_cleanup = 0; +} + static int fsl_bman_probe(struct platform_device *pdev) { int ret, err_irq; diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c index e4ef35abb508..923c44063a9a 100644 --- a/drivers/soc/fsl/qbman/bman_portal.c +++ b/drivers/soc/fsl/qbman/bman_portal.c @@ -100,7 +100,7 @@ static int bman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct bm_portal_config *pcfg; struct resource *addr_phys[2]; - int irq, cpu, err; + int irq, cpu, err, i; err = bman_is_probed(); if (!err) @@ -176,6 +176,22 @@ static int bman_portal_probe(struct platform_device *pdev) if (!cpu_online(cpu)) bman_offline_cpu(cpu); + if (__bman_portals_probed == 1 && bman_requires_cleanup()) { + /* + * BMan wasn't reset prior to boot (Kexec for example) + * Empty all the buffer pools so they are in reset state + */ + for (i = 0; i < BM_POOL_MAX; i++) { + err = bm_shutdown_pool(i); + if (err) { + dev_err(dev, "Failed to shutdown bpool %d\n", + i); + goto err_portal_init; + } + } + bman_done_cleanup(); + } + return 0; err_portal_init: diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h index 751ce90383b7..aa3981e04965 100644 --- a/drivers/soc/fsl/qbman/bman_priv.h +++ b/drivers/soc/fsl/qbman/bman_priv.h @@ -76,3 +76,8 @@ int bman_p_irqsource_add(struct bman_portal *p, u32 bits); const struct bm_portal_config * bman_get_bm_portal_config(const struct bman_portal *portal); + +int bman_requires_cleanup(void); +void bman_done_cleanup(void); + +int bm_shutdown_pool(u32 bpid); From c5501aa92a881e599e1dc80f387d27763d07f67c Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:16:57 +0000 Subject: [PATCH 071/123] soc/fsl/qbman: Cleanup QMan queues if device was already initialized If the QMan device was previously initialized make sure all the frame queues are out of service once all the portals are probed. This handles the case where the kernel is restarted without the SoC being reset (kexec for example) Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 4 ++-- drivers/soc/fsl/qbman/qman_ccsr.c | 13 ++++++++++++- drivers/soc/fsl/qbman/qman_portal.c | 18 +++++++++++++++++- drivers/soc/fsl/qbman/qman_priv.h | 7 +++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 636f83f781f5..f10f77dc04b9 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -2581,7 +2581,7 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s, #define qm_dqrr_drain_nomatch(p) \ _qm_dqrr_consume_and_match(p, 0, 0, false) -static int qman_shutdown_fq(u32 fqid) +int qman_shutdown_fq(u32 fqid) { struct qman_portal *p; struct device *dev; @@ -2754,7 +2754,7 @@ static int qman_shutdown_fq(u32 fqid) DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS); - if (mcr->result) { + if (mcr->result != QM_MCR_RESULT_OK) { dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n", fqid, mcr->result); ret = -EIO; diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index a3edefa792f8..709661b7b7a8 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -492,7 +492,7 @@ RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr); #endif -static unsigned int qm_get_fqid_maxcnt(void) +unsigned int qm_get_fqid_maxcnt(void) { return fqd_sz / 64; } @@ -737,6 +737,17 @@ int qman_is_probed(void) } EXPORT_SYMBOL_GPL(qman_is_probed); +int qman_requires_cleanup(void) +{ + return __qman_requires_cleanup; +} + +void qman_done_cleanup(void) +{ + __qman_requires_cleanup = 0; +} + + static int fsl_qman_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c index 991c35a72e00..5685b6706893 100644 --- a/drivers/soc/fsl/qbman/qman_portal.c +++ b/drivers/soc/fsl/qbman/qman_portal.c @@ -233,7 +233,7 @@ static int qman_portal_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct qm_portal_config *pcfg; struct resource *addr_phys[2]; - int irq, cpu, err; + int irq, cpu, err, i; u32 val; err = qman_is_probed(); @@ -323,6 +323,22 @@ static int qman_portal_probe(struct platform_device *pdev) if (!cpu_online(cpu)) qman_offline_cpu(cpu); + if (__qman_portals_probed == 1 && qman_requires_cleanup()) { + /* + * QMan wasn't reset prior to boot (Kexec for example) + * Empty all the frame queues so they are in reset state + */ + for (i = 0; i < qm_get_fqid_maxcnt(); i++) { + err = qman_shutdown_fq(i); + if (err) { + dev_err(dev, "Failed to shutdown frame queue %d\n", + i); + goto err_portal_init; + } + } + qman_done_cleanup(); + } + return 0; err_portal_init: diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index 04515718cfd9..a8a35fe9afc5 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -272,3 +272,10 @@ extern struct qman_portal *affine_portals[NR_CPUS]; extern struct qman_portal *qman_dma_portal; const struct qm_portal_config *qman_get_qm_portal_config( struct qman_portal *portal); + +unsigned int qm_get_fqid_maxcnt(void); + +int qman_shutdown_fq(u32 fqid); + +int qman_requires_cleanup(void); +void qman_done_cleanup(void); From 627da8bad530ec18c2111347fe559c5c21679220 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:16:59 +0000 Subject: [PATCH 072/123] soc/fsl/qbman: Fix drain_mr_fqni() The drain_mr_fqni() function may be called fron uninterruptable context so convert the msleep() to an mdelay(). Also ensure that the valid bit is updated while polling. Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index f10f77dc04b9..29895044f04d 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -1164,6 +1164,7 @@ static int drain_mr_fqrni(struct qm_portal *p) { const union qm_mr_entry *msg; loop: + qm_mr_pvb_update(p); msg = qm_mr_current(p); if (!msg) { /* @@ -1180,7 +1181,8 @@ loop: * entries well before the ring has been fully consumed, so * we're being *really* paranoid here. */ - msleep(1); + mdelay(1); + qm_mr_pvb_update(p); msg = qm_mr_current(p); if (!msg) return 0; From ea2b8488baa705be44e7c7ac8a42d33fd31315b7 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:17:01 +0000 Subject: [PATCH 073/123] soc/fsl/qbman: Disable interrupts during portal recovery Disable the QBMan interrupts during recovery. Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 22 +++++++++++++++++++--- drivers/soc/fsl/qbman/qman_ccsr.c | 1 + drivers/soc/fsl/qbman/qman_priv.h | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 29895044f04d..4a99ce5ce929 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -1070,6 +1070,20 @@ int qman_wq_alloc(void) return 0; } + +void qman_enable_irqs(void) +{ + int i; + + for (i = 0; i < num_possible_cpus(); i++) { + if (affine_portals[i]) { + qm_out(&affine_portals[i]->p, QM_REG_ISR, 0xffffffff); + qm_out(&affine_portals[i]->p, QM_REG_IIR, 0); + } + + } +} + /* * This is what everything can wait on, even if it migrates to a different cpu * to the one whose affine portal it is waiting on. @@ -1269,8 +1283,8 @@ static int qman_create_portal(struct qman_portal *portal, qm_out(p, QM_REG_ISDR, isdr); portal->irq_sources = 0; qm_out(p, QM_REG_IER, 0); - qm_out(p, QM_REG_ISR, 0xffffffff); snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu); + qm_out(p, QM_REG_IIR, 1); if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) { dev_err(c->dev, "request_irq() failed\n"); goto fail_irq; @@ -1290,7 +1304,7 @@ static int qman_create_portal(struct qman_portal *portal, isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI); qm_out(p, QM_REG_ISDR, isdr); if (qm_dqrr_current(p)) { - dev_err(c->dev, "DQRR unclean\n"); + dev_dbg(c->dev, "DQRR unclean\n"); qm_dqrr_cdc_consume_n(p, 0xffff); } if (qm_mr_current(p) && drain_mr_fqrni(p)) { @@ -1303,8 +1317,10 @@ static int qman_create_portal(struct qman_portal *portal, } /* Success */ portal->config = c; + qm_out(p, QM_REG_ISR, 0xffffffff); qm_out(p, QM_REG_ISDR, 0); - qm_out(p, QM_REG_IIR, 0); + if (!qman_requires_cleanup()) + qm_out(p, QM_REG_IIR, 0); /* Write a sane SDQCR */ qm_dqrr_sdqcr_set(p, portal->sdqcr); return 0; diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index 709661b7b7a8..157659fd033a 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -744,6 +744,7 @@ int qman_requires_cleanup(void) void qman_done_cleanup(void) { + qman_enable_irqs(); __qman_requires_cleanup = 0; } diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h index a8a35fe9afc5..fd1cf543fb81 100644 --- a/drivers/soc/fsl/qbman/qman_priv.h +++ b/drivers/soc/fsl/qbman/qman_priv.h @@ -279,3 +279,4 @@ int qman_shutdown_fq(u32 fqid); int qman_requires_cleanup(void); void qman_done_cleanup(void); +void qman_enable_irqs(void); From e844168a8f4a7de471059de3c4d60e4e770331e1 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:17:03 +0000 Subject: [PATCH 074/123] soc/fsl/qbman: Fixup qman_shutdown_fq() When shutting down a FQ on a dedicated channel only the SW portal associated with that channel can dequeue from it. Make sure the correct portal is use. Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/qman.c | 53 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c index 4a99ce5ce929..bf68d86d80ee 100644 --- a/drivers/soc/fsl/qbman/qman.c +++ b/drivers/soc/fsl/qbman/qman.c @@ -1018,6 +1018,20 @@ static inline void put_affine_portal(void) put_cpu_var(qman_affine_portal); } + +static inline struct qman_portal *get_portal_for_channel(u16 channel) +{ + int i; + + for (i = 0; i < num_possible_cpus(); i++) { + if (affine_portals[i] && + affine_portals[i]->config->channel == channel) + return affine_portals[i]; + } + + return NULL; +} + static struct workqueue_struct *qm_portal_wq; int qman_dqrr_set_ithresh(struct qman_portal *portal, u8 ithresh) @@ -2601,7 +2615,7 @@ static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s, int qman_shutdown_fq(u32 fqid) { - struct qman_portal *p; + struct qman_portal *p, *channel_portal; struct device *dev; union qm_mc_command *mcc; union qm_mc_result *mcr; @@ -2641,17 +2655,28 @@ int qman_shutdown_fq(u32 fqid) channel = qm_fqd_get_chan(&mcr->queryfq.fqd); wq = qm_fqd_get_wq(&mcr->queryfq.fqd); + if (channel < qm_channel_pool1) { + channel_portal = get_portal_for_channel(channel); + if (channel_portal == NULL) { + dev_err(dev, "Can't find portal for dedicated channel 0x%x\n", + channel); + ret = -EIO; + goto out; + } + } else + channel_portal = p; + switch (state) { case QM_MCR_NP_STATE_TEN_SCHED: case QM_MCR_NP_STATE_TRU_SCHED: case QM_MCR_NP_STATE_ACTIVE: case QM_MCR_NP_STATE_PARKED: orl_empty = 0; - mcc = qm_mc_start(&p->p); + mcc = qm_mc_start(&channel_portal->p); qm_fqid_set(&mcc->fq, fqid); - qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE); - if (!qm_mc_result_timeout(&p->p, &mcr)) { - dev_err(dev, "QUERYFQ_NP timeout\n"); + qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE); + if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) { + dev_err(dev, "ALTER_RETIRE timeout\n"); ret = -ETIMEDOUT; goto out; } @@ -2659,6 +2684,9 @@ int qman_shutdown_fq(u32 fqid) QM_MCR_VERB_ALTER_RETIRE); res = mcr->result; /* Make a copy as we reuse MCR below */ + if (res == QM_MCR_RESULT_OK) + drain_mr_fqrni(&channel_portal->p); + if (res == QM_MCR_RESULT_PENDING) { /* * Need to wait for the FQRN in the message ring, which @@ -2688,21 +2716,25 @@ int qman_shutdown_fq(u32 fqid) } /* Set the sdqcr to drain this channel */ if (channel < qm_channel_pool1) - qm_dqrr_sdqcr_set(&p->p, + qm_dqrr_sdqcr_set(&channel_portal->p, QM_SDQCR_TYPE_ACTIVE | QM_SDQCR_CHANNELS_DEDICATED); else - qm_dqrr_sdqcr_set(&p->p, + qm_dqrr_sdqcr_set(&channel_portal->p, QM_SDQCR_TYPE_ACTIVE | QM_SDQCR_CHANNELS_POOL_CONV (channel)); do { /* Keep draining DQRR while checking the MR*/ - qm_dqrr_drain_nomatch(&p->p); + qm_dqrr_drain_nomatch(&channel_portal->p); /* Process message ring too */ - found_fqrn = qm_mr_drain(&p->p, FQRN); + found_fqrn = qm_mr_drain(&channel_portal->p, + FQRN); cpu_relax(); } while (!found_fqrn); + /* Restore SDQCR */ + qm_dqrr_sdqcr_set(&channel_portal->p, + channel_portal->sdqcr); } if (res != QM_MCR_RESULT_OK && @@ -2733,9 +2765,8 @@ int qman_shutdown_fq(u32 fqid) * Wait for a dequeue and process the dequeues, * making sure to empty the ring completely */ - } while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY)); + } while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY)); } - qm_dqrr_sdqcr_set(&p->p, 0); while (!orl_empty) { /* Wait for the ORL to have been completely drained */ From 6ea4c0fe457094a62572a0b7ed781456a45b9a0d Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Thu, 1 Aug 2019 20:17:05 +0000 Subject: [PATCH 075/123] soc/fsl/qbman: Update device tree with reserved memory When using the reserved memory node in the device tree there are two options - dynamic or static. If a dynamic allocation was selected (where the kernel selects the address for the allocation) convert it to a static allocation by inserting the reg property. This will ensure the same memory is reused after a kexec() Signed-off-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/qbman/dpaa_sys.c | 60 +++++++++++++++++++------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c index 3e0a7f311693..9dd8bb571dbc 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.c +++ b/drivers/soc/fsl/qbman/dpaa_sys.c @@ -37,41 +37,53 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, size_t *size) { - int ret; struct device_node *mem_node; - u64 size64; struct reserved_mem *rmem; + struct property *prop; + int len, err; + __be32 *res_array; - ret = of_reserved_mem_device_init_by_idx(dev, dev->of_node, idx); - if (ret) { - dev_err(dev, - "of_reserved_mem_device_init_by_idx(%d) failed 0x%x\n", - idx, ret); - return -ENODEV; - } - mem_node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (mem_node) { - ret = of_property_read_u64(mem_node, "size", &size64); - if (ret) { - dev_err(dev, "of_address_to_resource fails 0x%x\n", - ret); - return -ENODEV; - } - *size = size64; - } else { + mem_node = of_parse_phandle(dev->of_node, "memory-region", idx); + if (!mem_node) { dev_err(dev, "No memory-region found for index %d\n", idx); return -ENODEV; } rmem = of_reserved_mem_lookup(mem_node); + if (!rmem) { + dev_err(dev, "of_reserved_mem_lookup() returned NULL\n"); + return -ENODEV; + } *addr = rmem->base; + *size = rmem->size; /* - * Disassociate the reserved memory area from the device - * because a device can only have one DMA memory area. This - * should be fine since the memory is allocated and initialized - * and only ever accessed by the QBMan device from now on + * Check if the reg property exists - if not insert the node + * so upon kexec() the same memory region address will be preserved. + * This is needed because QBMan HW does not allow the base address/ + * size to be modified once set. */ - of_reserved_mem_device_release(dev); + prop = of_find_property(mem_node, "reg", &len); + if (!prop) { + prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL); + if (!prop) + return -ENOMEM; + prop->value = res_array = devm_kzalloc(dev, sizeof(__be32) * 4, + GFP_KERNEL); + if (!prop->value) + return -ENOMEM; + res_array[0] = cpu_to_be32(upper_32_bits(*addr)); + res_array[1] = cpu_to_be32(lower_32_bits(*addr)); + res_array[2] = cpu_to_be32(upper_32_bits(*size)); + res_array[3] = cpu_to_be32(lower_32_bits(*size)); + prop->length = sizeof(__be32) * 4; + prop->name = devm_kstrdup(dev, "reg", GFP_KERNEL); + if (!prop->name) + return -ENOMEM; + err = of_add_property(mem_node, prop); + if (err) + return err; + } + return 0; } From 80f97619f5034888746ed11b01d275a90d75f7ee Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Wed, 14 Aug 2019 15:26:47 +0800 Subject: [PATCH 076/123] soc: fsl: guts: Add definition for LS1028A Adding compatible string "ls1028a-dcfg" to initialize guts driver for ls1028 and SoC die attribute definition for LS1028A Signed-off-by: Yinbo Zhu Signed-off-by: Li Yang --- drivers/soc/fsl/guts.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 1ef8068c8dd3..34810f9bb2ee 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -102,6 +102,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { .svr = 0x87360000, .mask = 0xff3f0000, }, + /* Die: LS1028A, SoC: LS1028A */ + { .die = "LS1028A", + .svr = 0x870b0000, + .mask = 0xff3f0000, + }, { }, }; @@ -224,6 +229,7 @@ static const struct of_device_id fsl_guts_of_match[] = { { .compatible = "fsl,ls1012a-dcfg", }, { .compatible = "fsl,ls1046a-dcfg", }, { .compatible = "fsl,lx2160a-dcfg", }, + { .compatible = "fsl,ls1028a-dcfg", }, {} }; MODULE_DEVICE_TABLE(of, fsl_guts_of_match); From c49a3a9cf26709d00211abbf79c2fd694ea4e86d Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 3 Jul 2019 17:56:39 +0300 Subject: [PATCH 077/123] soc: fsl: dpio: remove explicit device_link_remove Starting with commit 72175d4ea4c4 ("driver core: Make driver core own stateful device links") stateful device links are owned by the driver core and should not be explicitly removed on device unbind. Delete all device_link_remove appearances from the dpio driver. Signed-off-by: Ioana Ciornei Signed-off-by: Li Yang --- drivers/soc/fsl/dpio/dpio-service.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index b9539ef2c3cd..518a8e081b49 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c @@ -305,8 +305,6 @@ void dpaa2_io_service_deregister(struct dpaa2_io *service, list_del(&ctx->node); spin_unlock_irqrestore(&d->lock_notifications, irqflags); - if (dev) - device_link_remove(dev, d->dev); } EXPORT_SYMBOL_GPL(dpaa2_io_service_deregister); From eadf0b17b43db4e73a6bdde1ad745d3b582a71c5 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 3 Jul 2019 17:56:38 +0300 Subject: [PATCH 078/123] bus: fsl-mc: remove explicit device_link_del Starting with commit 72175d4ea4c4 ("driver core: Make driver core own stateful device links") stateful device links are owned by the driver core and should not be explicitly removed on device unbind. Delete all device_link_del appearances from the fsl-mc bus. Signed-off-by: Ioana Ciornei Acked-By: Laurentiu Tudor Signed-off-by: Li Yang --- drivers/bus/fsl-mc/fsl-mc-allocator.c | 1 - drivers/bus/fsl-mc/mc-io.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c index 8ad77246f322..cc7bb900f524 100644 --- a/drivers/bus/fsl-mc/fsl-mc-allocator.c +++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c @@ -330,7 +330,6 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev) fsl_mc_resource_free(resource); - device_link_del(mc_adev->consumer_link); mc_adev->consumer_link = NULL; } EXPORT_SYMBOL_GPL(fsl_mc_object_free); diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c index 3ae574a58cce..d9629fc13a15 100644 --- a/drivers/bus/fsl-mc/mc-io.c +++ b/drivers/bus/fsl-mc/mc-io.c @@ -255,7 +255,6 @@ void fsl_mc_portal_free(struct fsl_mc_io *mc_io) fsl_destroy_mc_io(mc_io); fsl_mc_resource_free(resource); - device_link_del(dpmcp_dev->consumer_link); dpmcp_dev->consumer_link = NULL; } EXPORT_SYMBOL_GPL(fsl_mc_portal_free); From 4a92f07816ba3020c690cc0f6e9627fec15b335e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 14 Aug 2019 10:23:16 +0200 Subject: [PATCH 079/123] bus: imx-weim: use module_platform_driver() Switch from module_platform_driver_probe() to module_platform_driver(). The former is not suitable for booting with device tree as the driver will be registered before the device and thus won't be probed again when the device is present. Signed-off-by: Sascha Hauer Signed-off-by: Shawn Guo --- drivers/bus/imx-weim.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index cb7d5504a22a..79af0c27f5a3 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -187,8 +187,7 @@ static int __init weim_timing_setup(struct device *dev, return 0; } -static int __init weim_parse_dt(struct platform_device *pdev, - void __iomem *base) +static int weim_parse_dt(struct platform_device *pdev, void __iomem *base) { const struct of_device_id *of_id = of_match_device(weim_id_table, &pdev->dev); @@ -233,7 +232,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, return ret; } -static int __init weim_probe(struct platform_device *pdev) +static int weim_probe(struct platform_device *pdev) { struct resource *res; struct clk *clk; @@ -270,8 +269,9 @@ static struct platform_driver weim_driver = { .name = "imx-weim", .of_match_table = weim_id_table, }, + .probe = weim_probe, }; -module_platform_driver_probe(weim_driver, weim_probe); +module_platform_driver(weim_driver); MODULE_AUTHOR("Freescale Semiconductor Inc."); MODULE_DESCRIPTION("i.MX EIM Controller Driver"); From da51ceda8ab054bf3b4c5cf86a8deec7d7849a5c Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Thu, 15 Aug 2019 11:43:54 +0530 Subject: [PATCH 080/123] soc: renesas: rcar-sysc: Add goto to of_node_put() before return The local variable np in function rcar_sysc_pd_init takes the return value of of_find_matching_node_and_match(), which gets a node but does not put it. If np is not put before the function returns, it may cause a memory leak. Hence, remove the return statement that does not immediately follow a putting of np. Replace it with a goto pointing to a pre-existing label that first puts np and then returns the required value. Issue found with Coccinelle. Fixes: afa6f53df6052968 ("soc: renesas: rcar-sysc: Add support for fixing up power area tables") Signed-off-by: Nishka Dasgupta Signed-off-by: Geert Uytterhoeven --- drivers/soc/renesas/rcar-sysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 02b29ea62dc4..54baa2fe8527 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -346,7 +346,7 @@ static int __init rcar_sysc_pd_init(void) if (info->init) { error = info->init(); if (error) - return error; + goto out_put; } has_cpg_mstp = of_find_compatible_node(NULL, NULL, From e0e1df61524f6c26421f813889934f63a1f6c06e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 9 Aug 2019 16:14:32 +0200 Subject: [PATCH 081/123] soc: renesas: rcar-sysc: Eliminate local variable gov MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of commit 980532a5dda319ee ("soc: renesas: rcar-sysc: Use GENPD_FLAG_ALWAYS_ON"), the local variable "gov" is assigned just once, so it can be eliminated. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Reviewed-by: Niklas Söderlund --- drivers/soc/renesas/rcar-sysc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 54baa2fe8527..59b5e6b10272 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -200,7 +200,6 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) { struct generic_pm_domain *genpd = &pd->genpd; const char *name = pd->genpd.name; - struct dev_power_governor *gov = &simple_qos_governor; int error; if (pd->flags & PD_CPU) { @@ -254,7 +253,7 @@ static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) rcar_sysc_power(&pd->ch, true); finalize: - error = pm_genpd_init(genpd, gov, false); + error = pm_genpd_init(genpd, &simple_qos_governor, false); if (error) pr_err("Failed to init PM domain %s: %d\n", name, error); From 3636e82135ce5ade87c238e61a315df23dbcd4d6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Aug 2019 23:21:51 +0100 Subject: [PATCH 082/123] soc: samsung: chipid: Fix memory leak in error path Currently when the call to product_id_to_soc_id fails there is a memory leak of soc_dev_attr->revision and soc_dev_attr on the error return path. Fix this by adding a common error return path that frees there obects and use this for two error return paths. Addresses-Coverity: ("Resource leak") Fixes: 3253b7b7cd44 ("soc: samsung: Add exynos chipid driver support") Signed-off-by: Colin Ian King Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 006a95feb618..75b6b880d9ef 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -81,15 +81,15 @@ int __init exynos_chipid_early_init(void) soc_dev_attr->soc_id = product_id_to_soc_id(product_id); if (!soc_dev_attr->soc_id) { pr_err("Unknown SoC\n"); - return -ENODEV; + ret = -ENODEV; + goto err; } /* please note that the actual registration will be deferred */ soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { - kfree(soc_dev_attr->revision); - kfree(soc_dev_attr); - return PTR_ERR(soc_dev); + ret = PTR_ERR(soc_dev); + goto err; } /* it is too early to use dev_info() here (soc_dev is NULL) */ @@ -97,5 +97,11 @@ int __init exynos_chipid_early_init(void) soc_dev_attr->soc_id, product_id, revision); return 0; + +err: + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr); + return ret; } + early_initcall(exynos_chipid_early_init); From c9cc9bec36d055077094db9fa57dfbf1870cbb73 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:47 +0200 Subject: [PATCH 083/123] soc: amlogic: meson-gx-socinfo: Add SM1 and S905X3 IDs Add the SoC IDs for the S905X3 Amlogic SM1 SoC. Reviewed-by: Martin Blumenstingl Reviewed-by: Jerome Brunet Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index ff86a75939e8..b9f4c6f4fd03 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -39,6 +39,7 @@ static const struct meson_gx_soc_id { { "TXHD", 0x27 }, { "G12A", 0x28 }, { "G12B", 0x29 }, + { "SM1", 0x2b }, }; static const struct meson_gx_package_id { @@ -66,6 +67,7 @@ static const struct meson_gx_package_id { { "S905X2", 0x28, 0x40, 0xf0 }, { "S922X", 0x29, 0x40, 0xf0 }, { "A311D", 0x29, 0x10, 0xf0 }, + { "S905X3", 0x2b, 0x5, 0xf }, }; static inline unsigned int socinfo_to_major(u32 socinfo) From b555cafb766a10846d228346aa71e517a5393c95 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:48 +0200 Subject: [PATCH 084/123] dt-bindings: soc: amlogic: clk-measure: Add SM1 compatible Add the Amlogic SM1 Compatible for the clk-measurer IP. Reviewed-by: Martin Blumenstingl Reviewed-by: Rob Herring Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt index 6bf6b43f8dd8..3dd563cec794 100644 --- a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt +++ b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt @@ -11,6 +11,7 @@ Required properties: "amlogic,meson8b-clk-measure" for Meson8b SoCs "amlogic,meson-axg-clk-measure" for AXG SoCs "amlogic,meson-g12a-clk-measure" for G12a SoCs + "amlogic,meson-sm1-clk-measure" for SM1 SoCs - reg: base address and size of the Clock Measurer register space. Example: From 623699711a39134a2b2cd6bdba81c5167deceec8 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 20 Aug 2019 16:40:49 +0200 Subject: [PATCH 085/123] soc: amlogic: clk-measure: Add support for SM1 Add the clk-measurer clocks IDs for the Amlogic SM1 SoC family. Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-clk-measure.c | 134 ++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index f09b404b39d3..0fa47d77577d 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -357,6 +357,136 @@ static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { CLK_MSR_ID(122, "audio_pdm_dclk"), }; +static struct meson_msr_id clk_msr_sm1[CLK_MSR_MAX] = { + CLK_MSR_ID(0, "ring_osc_out_ee_0"), + CLK_MSR_ID(1, "ring_osc_out_ee_1"), + CLK_MSR_ID(2, "ring_osc_out_ee_2"), + CLK_MSR_ID(3, "ring_osc_out_ee_3"), + CLK_MSR_ID(4, "gp0_pll"), + CLK_MSR_ID(5, "gp1_pll"), + CLK_MSR_ID(6, "enci"), + CLK_MSR_ID(7, "clk81"), + CLK_MSR_ID(8, "encp"), + CLK_MSR_ID(9, "encl"), + CLK_MSR_ID(10, "vdac"), + CLK_MSR_ID(11, "eth_tx"), + CLK_MSR_ID(12, "hifi_pll"), + CLK_MSR_ID(13, "mod_tcon"), + CLK_MSR_ID(14, "fec_0"), + CLK_MSR_ID(15, "fec_1"), + CLK_MSR_ID(16, "fec_2"), + CLK_MSR_ID(17, "sys_pll_div16"), + CLK_MSR_ID(18, "sys_cpu_div16"), + CLK_MSR_ID(19, "lcd_an_ph2"), + CLK_MSR_ID(20, "rtc_osc_out"), + CLK_MSR_ID(21, "lcd_an_ph3"), + CLK_MSR_ID(22, "eth_phy_ref"), + CLK_MSR_ID(23, "mpll_50m"), + CLK_MSR_ID(24, "eth_125m"), + CLK_MSR_ID(25, "eth_rmii"), + CLK_MSR_ID(26, "sc_int"), + CLK_MSR_ID(27, "in_mac"), + CLK_MSR_ID(28, "sar_adc"), + CLK_MSR_ID(29, "pcie_inp"), + CLK_MSR_ID(30, "pcie_inn"), + CLK_MSR_ID(31, "mpll_test_out"), + CLK_MSR_ID(32, "vdec"), + CLK_MSR_ID(34, "eth_mpll_50m"), + CLK_MSR_ID(35, "mali"), + CLK_MSR_ID(36, "hdmi_tx_pixel"), + CLK_MSR_ID(37, "cdac"), + CLK_MSR_ID(38, "vdin_meas"), + CLK_MSR_ID(39, "bt656"), + CLK_MSR_ID(40, "arm_ring_osc_out_4"), + CLK_MSR_ID(41, "eth_rx_or_rmii"), + CLK_MSR_ID(42, "mp0_out"), + CLK_MSR_ID(43, "fclk_div5"), + CLK_MSR_ID(44, "pwm_b"), + CLK_MSR_ID(45, "pwm_a"), + CLK_MSR_ID(46, "vpu"), + CLK_MSR_ID(47, "ddr_dpll_pt"), + CLK_MSR_ID(48, "mp1_out"), + CLK_MSR_ID(49, "mp2_out"), + CLK_MSR_ID(50, "mp3_out"), + CLK_MSR_ID(51, "sd_emmc_c"), + CLK_MSR_ID(52, "sd_emmc_b"), + CLK_MSR_ID(53, "sd_emmc_a"), + CLK_MSR_ID(54, "vpu_clkc"), + CLK_MSR_ID(55, "vid_pll_div_out"), + CLK_MSR_ID(56, "wave420l_a"), + CLK_MSR_ID(57, "wave420l_c"), + CLK_MSR_ID(58, "wave420l_b"), + CLK_MSR_ID(59, "hcodec"), + CLK_MSR_ID(60, "arm_ring_osc_out_5"), + CLK_MSR_ID(61, "gpio_msr"), + CLK_MSR_ID(62, "hevcb"), + CLK_MSR_ID(63, "dsi_meas"), + CLK_MSR_ID(64, "spicc_1"), + CLK_MSR_ID(65, "spicc_0"), + CLK_MSR_ID(66, "vid_lock"), + CLK_MSR_ID(67, "dsi_phy"), + CLK_MSR_ID(68, "hdcp22_esm"), + CLK_MSR_ID(69, "hdcp22_skp"), + CLK_MSR_ID(70, "pwm_f"), + CLK_MSR_ID(71, "pwm_e"), + CLK_MSR_ID(72, "pwm_d"), + CLK_MSR_ID(73, "pwm_c"), + CLK_MSR_ID(74, "arm_ring_osc_out_6"), + CLK_MSR_ID(75, "hevcf"), + CLK_MSR_ID(76, "arm_ring_osc_out_7"), + CLK_MSR_ID(77, "rng_ring_osc_0"), + CLK_MSR_ID(78, "rng_ring_osc_1"), + CLK_MSR_ID(79, "rng_ring_osc_2"), + CLK_MSR_ID(80, "rng_ring_osc_3"), + CLK_MSR_ID(81, "vapb"), + CLK_MSR_ID(82, "ge2d"), + CLK_MSR_ID(83, "co_rx"), + CLK_MSR_ID(84, "co_tx"), + CLK_MSR_ID(85, "arm_ring_osc_out_8"), + CLK_MSR_ID(86, "arm_ring_osc_out_9"), + CLK_MSR_ID(87, "mipi_dsi_phy"), + CLK_MSR_ID(88, "cis2_adapt"), + CLK_MSR_ID(89, "hdmi_todig"), + CLK_MSR_ID(90, "hdmitx_sys"), + CLK_MSR_ID(91, "nna_core"), + CLK_MSR_ID(92, "nna_axi"), + CLK_MSR_ID(93, "vad"), + CLK_MSR_ID(94, "eth_phy_rx"), + CLK_MSR_ID(95, "eth_phy_pll"), + CLK_MSR_ID(96, "vpu_b"), + CLK_MSR_ID(97, "cpu_b_tmp"), + CLK_MSR_ID(98, "ts"), + CLK_MSR_ID(99, "arm_ring_osc_out_10"), + CLK_MSR_ID(100, "arm_ring_osc_out_11"), + CLK_MSR_ID(101, "arm_ring_osc_out_12"), + CLK_MSR_ID(102, "arm_ring_osc_out_13"), + CLK_MSR_ID(103, "arm_ring_osc_out_14"), + CLK_MSR_ID(104, "arm_ring_osc_out_15"), + CLK_MSR_ID(105, "arm_ring_osc_out_16"), + CLK_MSR_ID(106, "ephy_test"), + CLK_MSR_ID(107, "au_dac_g128x"), + CLK_MSR_ID(108, "audio_locker_out"), + CLK_MSR_ID(109, "audio_locker_in"), + CLK_MSR_ID(110, "audio_tdmout_c_sclk"), + CLK_MSR_ID(111, "audio_tdmout_b_sclk"), + CLK_MSR_ID(112, "audio_tdmout_a_sclk"), + CLK_MSR_ID(113, "audio_tdmin_lb_sclk"), + CLK_MSR_ID(114, "audio_tdmin_c_sclk"), + CLK_MSR_ID(115, "audio_tdmin_b_sclk"), + CLK_MSR_ID(116, "audio_tdmin_a_sclk"), + CLK_MSR_ID(117, "audio_resample"), + CLK_MSR_ID(118, "audio_pdm_sys"), + CLK_MSR_ID(119, "audio_spdifout_b"), + CLK_MSR_ID(120, "audio_spdifout"), + CLK_MSR_ID(121, "audio_spdifin"), + CLK_MSR_ID(122, "audio_pdm_dclk"), + CLK_MSR_ID(123, "audio_resampled"), + CLK_MSR_ID(124, "earcrx_pll"), + CLK_MSR_ID(125, "earcrx_pll_test"), + CLK_MSR_ID(126, "csi_phy0"), + CLK_MSR_ID(127, "csi2_data"), +}; + static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int duration) { @@ -545,6 +675,10 @@ static const struct of_device_id meson_msr_match_table[] = { .compatible = "amlogic,meson-g12a-clk-measure", .data = (void *)clk_msr_g12a, }, + { + .compatible = "amlogic,meson-sm1-clk-measure", + .data = (void *)clk_msr_sm1, + }, { /* sentinel */ } }; From 49ed86f503be80aac158a567c4cfd31cf1cd181e Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Mon, 19 Aug 2019 12:57:06 +0530 Subject: [PATCH 086/123] soc: amlogic: meson-gx-socinfo: Add of_node_put() before return The variable np in function meson_gx_socinfo_init takes the return value of of_find_compatible_node, which gets a node but does not put it. If this node is not put it may cause a memory leak. Hence put np after its usefulness has been exhausted. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Reviewed-by: Neil Armstrong Fixes: a9daaba2965e ("soc: Add Amlogic SoC Information driver") Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index b9f4c6f4fd03..6d0d04f163cb 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -141,8 +141,10 @@ static int __init meson_gx_socinfo_init(void) } /* check if chip-id is available */ - if (!of_property_read_bool(np, "amlogic,has-chip-id")) + if (!of_property_read_bool(np, "amlogic,has-chip-id")) { + of_node_put(np); return -ENODEV; + } /* node should be a syscon */ regmap = syscon_node_to_regmap(np); From f117249e4b7831248404ff1fb47bfe44c355caea Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 12:39:51 +0530 Subject: [PATCH 087/123] soc: qcom: smem: Update max processor count Update max processor count to reflect the number of co-processors on SC7180 SoCs. Reviewed-by: Vinod Koul Tested-by: Vinod Koul Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 0caf67e2f037..28c19bcb2f20 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -84,7 +84,7 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 10 +#define SMEM_HOST_COUNT 11 /** * struct smem_proc_comm - proc_comm communication struct (legacy) From 2aabdf3b50eabad59107f6adfc24436d9bda4fb9 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 12:39:52 +0530 Subject: [PATCH 088/123] dt-bindings: firmware: scm: re-order compatible list re-order compatible list to maintain sort order. Tested-by: Vinod Koul Reviewed-by: Rob Herring Reviewed-by: Vinod Koul Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index 41f133a4e2fa..d19be836df53 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -9,13 +9,13 @@ Required properties: - compatible: must contain one of the following: * "qcom,scm-apq8064" * "qcom,scm-apq8084" + * "qcom,scm-ipq4019" * "qcom,scm-msm8660" * "qcom,scm-msm8916" * "qcom,scm-msm8960" * "qcom,scm-msm8974" * "qcom,scm-msm8996" * "qcom,scm-msm8998" - * "qcom,scm-ipq4019" * "qcom,scm-sdm845" and: * "qcom,scm" From e6fdc7ad66f115a24e00058299297c33ba34343a Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 12:39:53 +0530 Subject: [PATCH 089/123] dt-bindings: firmware: scm: Add SM8150 and SC7180 support Add compatible for SM8150 and SC7180 SoCs. Tested-by: Vinod Koul Reviewed-by: Rob Herring Reviewed-by: Vinod Koul Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.txt b/Documentation/devicetree/bindings/firmware/qcom,scm.txt index d19be836df53..3f29ea04b5fe 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.txt +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.txt @@ -16,7 +16,9 @@ Required properties: * "qcom,scm-msm8974" * "qcom,scm-msm8996" * "qcom,scm-msm8998" + * "qcom,scm-sc7180" * "qcom,scm-sdm845" + * "qcom,scm-sm8150" and: * "qcom,scm" - clocks: Specifies clocks needed by the SCM interface, if any: From a1c4743e990d9a4a3254b4d13be2921f59bee019 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 12:39:56 +0530 Subject: [PATCH 090/123] dt-bindings: soc: qcom: aoss: Add SM8150 and SC7180 support Add SM8150 and SC7180 AOSS QMP to the list of possible bindings. Tested-by: Vinod Koul Reviewed-by: Rob Herring Reviewed-by: Vinod Koul Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt index 954ffee0a9c4..4fc571e78f01 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt @@ -15,7 +15,10 @@ power-domains. - compatible: Usage: required Value type: - Definition: must be "qcom,sdm845-aoss-qmp" + Definition: must be one of: + "qcom,sc7180-aoss-qmp" + "qcom,sdm845-aoss-qmp" + "qcom,sm8150-aoss-qmp" - reg: Usage: required From 1709510221c57fd566479c228434ff9edd6435be Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 12:39:57 +0530 Subject: [PATCH 091/123] soc: qcom: aoss: Add AOSS QMP support Add AOSS QMP support for SM8150 and SC7180 SoCs. Reviewed-by: Vinod Koul Tested-by: Vinod Koul Signed-off-by: Sibi Sankar Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 443dab2207de..33a27e6c6d67 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -592,7 +592,9 @@ static int qmp_remove(struct platform_device *pdev) } static const struct of_device_id qmp_dt_match[] = { + { .compatible = "qcom,sc7180-aoss-qmp", }, { .compatible = "qcom,sdm845-aoss-qmp", }, + { .compatible = "qcom,sm8150-aoss-qmp", }, {} }; MODULE_DEVICE_TABLE(of, qmp_dt_match); From 823034575c177d1d69673102a994d5d33b05e3de Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 21 Aug 2019 17:05:39 +0200 Subject: [PATCH 092/123] soc: samsung: chipid: Remove the regmap lookup error log In commit 40d8aff614f7 ("soc: samsung: chipid: Convert exynos-chipid driver to use the regmap API") of_find_compatible_node() call was substituted with syscon_regmap_lookup_by_compatible() but also an error log was added for case where lookup fails. On multiplatform the lookup will always fail on any non-samsung device so the log is incorrect. Remove the error log and just return an error code from syscon_regmap_lookup_by_compatible() which internally calls of_find_compatible_node(). Reported-by: Jon Hunter Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-chipid.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 75b6b880d9ef..c55a47cfe617 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -56,10 +56,8 @@ int __init exynos_chipid_early_init(void) int ret; regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid"); - if (IS_ERR(regmap)) { - pr_err("Failed to get CHIPID regmap\n"); + if (IS_ERR(regmap)) return PTR_ERR(regmap); - } ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); if (ret < 0) From 28578825ede61834a2c46e7f9a89012c4c7a667f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 21 Aug 2019 17:39:26 +0200 Subject: [PATCH 093/123] soc: samsung: chipid: Select missing dependency for EXYNOS_CHIPID The chipid driver uses the MFD syscon API but it was not covered properly in Kconfig. Signed-off-by: Sylwester Nawrocki Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 2905f5262197..33ad0de2de3c 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -10,6 +10,7 @@ if SOC_SAMSUNG config EXYNOS_CHIPID bool "Exynos Chipid controller driver" if COMPILE_TEST depends on ARCH_EXYNOS || COMPILE_TEST + select MFD_SYSCON select SOC_BUS config EXYNOS_PMU From af0bc634728c0bc6a3f66f911f227d5c6396db88 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 9 Aug 2019 15:43:07 +0200 Subject: [PATCH 094/123] soc: renesas: rmobile-sysc: Set GENPD_FLAG_ALWAYS_ON for always-on domain Currently the R-Mobile "always-on" PM Domain is implemented by returning -EBUSY from the generic_pm_domain.power_off() callback, and doing nothing in the generic_pm_domain.power_on() callback. However, this means the PM Domain core code is not aware of the semantics of this special domain, leading to boot warnings like the following on SH/R-Mobile SoCs: sh_cmt e6130000.timer: PM domain c5 will not be powered off Fix this by making the always-on nature of the domain explicit instead, by setting the GENPD_FLAG_ALWAYS_ON flag. This removes the need for the domain to provide power control callbacks. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Reviewed-by: Ulf Hansson --- drivers/soc/renesas/rmobile-sysc.c | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/soc/renesas/rmobile-sysc.c b/drivers/soc/renesas/rmobile-sysc.c index 421ae1c887d8..54b616ad4a62 100644 --- a/drivers/soc/renesas/rmobile-sysc.c +++ b/drivers/soc/renesas/rmobile-sysc.c @@ -48,12 +48,8 @@ struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) static int rmobile_pd_power_down(struct generic_pm_domain *genpd) { struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); - unsigned int mask; + unsigned int mask = BIT(rmobile_pd->bit_shift); - if (rmobile_pd->bit_shift == ~0) - return -EBUSY; - - mask = BIT(rmobile_pd->bit_shift); if (rmobile_pd->suspend) { int ret = rmobile_pd->suspend(); @@ -80,14 +76,10 @@ static int rmobile_pd_power_down(struct generic_pm_domain *genpd) static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd) { - unsigned int mask; + unsigned int mask = BIT(rmobile_pd->bit_shift); unsigned int retry_count; int ret = 0; - if (rmobile_pd->bit_shift == ~0) - return 0; - - mask = BIT(rmobile_pd->bit_shift); if (__raw_readl(rmobile_pd->base + PSTR) & mask) return ret; @@ -122,11 +114,15 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) struct dev_power_governor *gov = rmobile_pd->gov; genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - genpd->power_off = rmobile_pd_power_down; - genpd->power_on = rmobile_pd_power_up; - genpd->attach_dev = cpg_mstp_attach_dev; - genpd->detach_dev = cpg_mstp_detach_dev; - __rmobile_pd_power_up(rmobile_pd); + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; + + if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) { + genpd->power_off = rmobile_pd_power_down; + genpd->power_on = rmobile_pd_power_up; + __rmobile_pd_power_up(rmobile_pd); + } + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); } @@ -270,6 +266,11 @@ static void __init rmobile_setup_pm_domain(struct device_node *np, break; case PD_NORMAL: + if (pd->bit_shift == ~0) { + /* Top-level always-on domain */ + pr_debug("PM domain %s is always-on domain\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + } break; } From 435dce2da218b0eb1ff758b77831562845394848 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 19 Aug 2019 19:05:58 +0200 Subject: [PATCH 095/123] soc: renesas: Enable ARM_ERRATA_814220 for affected Cortex-A7 ARM Erratum 814220 affects Cortex-A7 revisions r0p2-r0p5. Automatically enable support code to mitigate the erratum when compiling a kernel for any of the affected Renesas SoCs: - R-Mobile APE6: r0p2, - RZ/G1E: r0p5, - RZ/G1C: r0p5, - R-Car H2: r0p3, - R-Car E2: r0p5, - RZ/N1: r0p5. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/soc/renesas/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2bbf49e5d441..a72d014ea37c 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -72,6 +72,7 @@ config ARCH_R8A73A4 bool "R-Mobile APE6 (R8A73A40)" select ARCH_RMOBILE select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 select HAVE_ARM_ARCH_TIMER select RENESAS_IRQC @@ -95,11 +96,13 @@ config ARCH_R8A7744 config ARCH_R8A7745 bool "RZ/G1E (R8A77450)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A7745 config ARCH_R8A77470 bool "RZ/G1C (R8A77470)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A77470 config ARCH_R8A7778 @@ -117,6 +120,7 @@ config ARCH_R8A7790 bool "R-Car H2 (R8A77900)" select ARCH_RCAR_GEN2 select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 select I2C select SYSC_R8A7790 @@ -143,11 +147,13 @@ config ARCH_R8A7793 config ARCH_R8A7794 bool "R-Car E2 (R8A77940)" select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 select SYSC_R8A7794 config ARCH_R9A06G032 bool "RZ/N1D (R9A06G032)" select ARCH_RZN1 + select ARM_ERRATA_814220 config ARCH_SH73A0 bool "SH-Mobile AG5 (R8A73A00)" From 2eced4607a1e6f51f928ae3e521fe02be5cb7d23 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 19 Aug 2019 18:54:02 +0200 Subject: [PATCH 096/123] soc: renesas: Enable ARM_ERRATA_754322 for affected Cortex-A9 ARM Erratum 754322 affects Cortex-A9 revisions r2p* and r3p*. Automatically enable support code to mitigate the erratum when compiling a kernel for any of the affected Renesas SoCs: - RZ/A1: r3p0, - R-Mobile A1: r2p4, - R-Car M1A: r2p2-00rel0, - R-Car H1: r3p0, - SH-Mobile AG5: r2p2. EMMA Mobile EV2 (r1p3) and RZ/A2 (r4p1) are not affected. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman --- drivers/soc/renesas/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index a72d014ea37c..3c5e017bacba 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -55,6 +55,7 @@ config ARCH_EMEV2 config ARCH_R7S72100 bool "RZ/A1H (R7S72100)" + select ARM_ERRATA_754322 select PM select PM_GENERIC_DOMAINS select RENESAS_OSTM @@ -79,6 +80,7 @@ config ARCH_R8A73A4 config ARCH_R8A7740 bool "R-Mobile A1 (R8A77400)" select ARCH_RMOBILE + select ARM_ERRATA_754322 select RENESAS_INTC_IRQPIN config ARCH_R8A7743 @@ -108,10 +110,12 @@ config ARCH_R8A77470 config ARCH_R8A7778 bool "R-Car M1A (R8A77781)" select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 config ARCH_R8A7779 bool "R-Car H1 (R8A77790)" select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP select SYSC_R8A7779 @@ -158,6 +162,7 @@ config ARCH_R9A06G032 config ARCH_SH73A0 bool "SH-Mobile AG5 (R8A73A00)" select ARCH_RMOBILE + select ARM_ERRATA_754322 select HAVE_ARM_SCU if SMP select HAVE_ARM_TWD if SMP select RENESAS_INTC_IRQPIN From 1a92f989126ef0743650d43f75bfc431a5c3a310 Mon Sep 17 00:00:00 2001 From: Bibby Hsieh Date: Tue, 20 Aug 2019 16:49:27 +0800 Subject: [PATCH 097/123] soc: mediatek: cmdq: reorder the parameter The order of gce instructions is [subsys offset value] so reorder the parameter of cmdq_pkt_write_mask and cmdq_pkt_write function. Signed-off-by: Bibby Hsieh Reviewed-by: CK Hu Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-cmdq-helper.c | 6 +++--- include/linux/soc/mediatek/mtk-cmdq.h | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index ff9fef5a032b..082b8978651e 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -136,7 +136,7 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code, return 0; } -int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset) +int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value) { u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) | (subsys << CMDQ_SUBSYS_SHIFT); @@ -145,8 +145,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset) } EXPORT_SYMBOL(cmdq_pkt_write); -int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, - u32 subsys, u32 offset, u32 mask) +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, + u32 offset, u32 value, u32 mask) { u32 offset_mask = offset; int err = 0; diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 54ade13a9b15..4bba1c8d97fa 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -63,26 +63,26 @@ void cmdq_pkt_destroy(struct cmdq_pkt *pkt); /** * cmdq_pkt_write() - append write command to the CMDQ packet * @pkt: the CMDQ packet - * @value: the specified target register value * @subsys: the CMDQ sub system code * @offset: register offset from CMDQ sub system + * @value: the specified target register value * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 value, u32 subsys, u32 offset); +int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value); /** * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet * @pkt: the CMDQ packet - * @value: the specified target register value * @subsys: the CMDQ sub system code * @offset: register offset from CMDQ sub system + * @value: the specified target register value * @mask: the specified target register mask * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 value, - u32 subsys, u32 offset, u32 mask); +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, + u32 offset, u32 value, u32 mask); /** * cmdq_pkt_wfe() - append wait for event command to the CMDQ packet From 556030f0604f3cf5f1ea91307c0695541e5c74ca Mon Sep 17 00:00:00 2001 From: Bibby Hsieh Date: Tue, 20 Aug 2019 16:49:28 +0800 Subject: [PATCH 098/123] soc: mediatek: cmdq: change the type of input parameter According to the cmdq hardware design, the subsys is u8, the offset is u16 and the event id is u16. This patch changes the type of subsys, offset and event id to the correct type. Signed-off-by: Bibby Hsieh Reviewed-by: CK Hu Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-cmdq-helper.c | 10 +++++----- include/linux/soc/mediatek/mtk-cmdq.h | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 082b8978651e..7aa0517ff2f3 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -136,7 +136,7 @@ static int cmdq_pkt_append_command(struct cmdq_pkt *pkt, enum cmdq_code code, return 0; } -int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value) +int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) { u32 arg_a = (offset & CMDQ_ARG_A_WRITE_MASK) | (subsys << CMDQ_SUBSYS_SHIFT); @@ -145,8 +145,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value) } EXPORT_SYMBOL(cmdq_pkt_write); -int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, - u32 offset, u32 value, u32 mask) +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, + u16 offset, u32 value, u32 mask) { u32 offset_mask = offset; int err = 0; @@ -161,7 +161,7 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, } EXPORT_SYMBOL(cmdq_pkt_write_mask); -int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event) +int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event) { u32 arg_b; @@ -181,7 +181,7 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event) } EXPORT_SYMBOL(cmdq_pkt_wfe); -int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event) +int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) { if (event >= CMDQ_MAX_EVENT) return -EINVAL; diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 4bba1c8d97fa..f3ae45d02e80 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -69,7 +69,7 @@ void cmdq_pkt_destroy(struct cmdq_pkt *pkt); * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value); +int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value); /** * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet @@ -81,8 +81,8 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u32 subsys, u32 offset, u32 value); * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, - u32 offset, u32 value, u32 mask); +int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, + u16 offset, u32 value, u32 mask); /** * cmdq_pkt_wfe() - append wait for event command to the CMDQ packet @@ -91,7 +91,7 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u32 subsys, * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event); +int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event); /** * cmdq_pkt_clear_event() - append clear event command to the CMDQ packet @@ -100,7 +100,7 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u32 event); * * Return: 0 for success; else the error code is returned */ -int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u32 event); +int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event); /** * cmdq_pkt_flush_async() - trigger CMDQ to asynchronously execute the CMDQ From 968c6f4b3c21e9fae9b49cc91214a01b4827625d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 21 Aug 2019 18:33:04 +0200 Subject: [PATCH 099/123] soc: imx: gpcv2: Print the correct error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code prints 'ret' (thus 0) while it should use 'err'. Signed-off-by: Guido Günther Reviewed-by: Daniel Baluta Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 31b8d002d855..b0dffb06c05d 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -198,7 +198,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, err = regulator_disable(domain->regulator); if (err) dev_err(domain->dev, - "failed to disable regulator: %d\n", ret); + "failed to disable regulator: %d\n", err); /* Preserve earlier error code */ ret = ret ?: err; } From 7b5ab6437961f5e37ab9adfae18cf1671e39e7a3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:17 +0200 Subject: [PATCH 100/123] ARM: davinci: da850-evm: model the backlight GPIO as an actual device Instead of enabling the panel backlight in a callback defined in board file using deprecated legacy GPIO API calls, model the line as a GPIO backlight device. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 42 +++++++++++++++++-------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 0628e7d7dcf3..ac05d4838f1e 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -804,34 +805,49 @@ static const short da850_evm_mmcsd0_pins[] __initconst = { static void da850_panel_power_ctrl(int val) { - /* lcd backlight */ - gpio_set_value(DA850_LCD_BL_PIN, val); - /* lcd power */ gpio_set_value(DA850_LCD_PWR_PIN, val); } +static struct property_entry da850_lcd_backlight_props[] = { + PROPERTY_ENTRY_BOOL("default-on"), + { } +}; + +static struct gpiod_lookup_table da850_lcd_backlight_gpio_table = { + .dev_id = "gpio-backlight", + .table = { + GPIO_LOOKUP("davinci_gpio", DA850_LCD_BL_PIN, NULL, 0), + { } + }, +}; + +static const struct platform_device_info da850_lcd_backlight_info = { + .name = "gpio-backlight", + .id = PLATFORM_DEVID_NONE, + .properties = da850_lcd_backlight_props, +}; + static int da850_lcd_hw_init(void) { + struct platform_device *backlight; int status; - status = gpio_request(DA850_LCD_BL_PIN, "lcd bl"); + gpiod_add_lookup_table(&da850_lcd_backlight_gpio_table); + backlight = platform_device_register_full(&da850_lcd_backlight_info); + if (IS_ERR(backlight)) + return PTR_ERR(backlight); + + status = gpio_request(DA850_LCD_PWR_PIN, "lcd pwr"); if (status < 0) return status; - status = gpio_request(DA850_LCD_PWR_PIN, "lcd pwr"); - if (status < 0) { - gpio_free(DA850_LCD_BL_PIN); - return status; - } - - gpio_direction_output(DA850_LCD_BL_PIN, 0); gpio_direction_output(DA850_LCD_PWR_PIN, 0); - /* Switch off panel power and backlight */ + /* Switch off panel power */ da850_panel_power_ctrl(0); - /* Switch on panel power and backlight */ + /* Switch on panel power */ da850_panel_power_ctrl(1); return 0; From 611097d5daea95688e9187a53f36a8c743eaa0b8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:18 +0200 Subject: [PATCH 101/123] fbdev: da8xx: add support for a regulator We want to remove the hacky platform data callback for power control. Add a regulator to the driver data and enable/disable it next to the current panel_power_ctrl() calls. We will use it in subsequent patch on da850-evm. Signed-off-by: Bartosz Golaszewski Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sekhar Nori --- drivers/video/fbdev/da8xx-fb.c | 54 ++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index b1cf248f3291..02dfe9e32eed 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,7 @@ struct da8xx_fb_par { #endif unsigned int lcdc_clk_rate; void (*panel_power_ctrl)(int); + struct regulator *lcd_supply; u32 pseudo_palette[16]; struct fb_videomode mode; struct lcd_ctrl_config cfg; @@ -1066,6 +1068,7 @@ static void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par) static int fb_remove(struct platform_device *dev) { struct fb_info *info = dev_get_drvdata(&dev->dev); + int ret; if (info) { struct da8xx_fb_par *par = info->par; @@ -1073,8 +1076,13 @@ static int fb_remove(struct platform_device *dev) #ifdef CONFIG_CPU_FREQ lcd_da8xx_cpufreq_deregister(par); #endif - if (par->panel_power_ctrl) + if (par->panel_power_ctrl) { par->panel_power_ctrl(0); + } else if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } lcd_disable_raster(DA8XX_FRAME_WAIT); lcdc_write(0, LCD_RASTER_CTRL_REG); @@ -1179,15 +1187,25 @@ static int cfb_blank(int blank, struct fb_info *info) case FB_BLANK_UNBLANK: lcd_enable_raster(); - if (par->panel_power_ctrl) + if (par->panel_power_ctrl) { par->panel_power_ctrl(1); + } else if (par->lcd_supply) { + ret = regulator_enable(par->lcd_supply); + if (ret) + return ret; + } break; case FB_BLANK_NORMAL: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_POWERDOWN: - if (par->panel_power_ctrl) + if (par->panel_power_ctrl) { par->panel_power_ctrl(0); + } else if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } lcd_disable_raster(DA8XX_FRAME_WAIT); break; @@ -1400,6 +1418,20 @@ static int fb_probe(struct platform_device *device) par->panel_power_ctrl(1); } + par->lcd_supply = devm_regulator_get_optional(&device->dev, "lcd"); + if (IS_ERR(par->lcd_supply)) { + if (PTR_ERR(par->lcd_supply) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_pm_runtime_disable; + } + + par->lcd_supply = NULL; + } else { + ret = regulator_enable(par->lcd_supply); + if (ret) + goto err_pm_runtime_disable; + } + fb_videomode_to_var(&da8xx_fb_var, lcdc_info); par->cfg = *lcd_cfg; @@ -1603,10 +1635,16 @@ static int fb_suspend(struct device *dev) { struct fb_info *info = dev_get_drvdata(dev); struct da8xx_fb_par *par = info->par; + int ret; console_lock(); - if (par->panel_power_ctrl) + if (par->panel_power_ctrl) { par->panel_power_ctrl(0); + } else if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; + } fb_set_suspend(info, 1); lcd_disable_raster(DA8XX_FRAME_WAIT); @@ -1620,6 +1658,7 @@ static int fb_resume(struct device *dev) { struct fb_info *info = dev_get_drvdata(dev); struct da8xx_fb_par *par = info->par; + int ret; console_lock(); pm_runtime_get_sync(dev); @@ -1627,8 +1666,13 @@ static int fb_resume(struct device *dev) if (par->blank == FB_BLANK_UNBLANK) { lcd_enable_raster(); - if (par->panel_power_ctrl) + if (par->panel_power_ctrl) { par->panel_power_ctrl(1); + } else if (par->lcd_supply) { + ret = regulator_enable(par->lcd_supply); + if (ret) + return ret; + } } fb_set_suspend(info, 0); From 9c65754cd32ffebf1f2adf4e740d2688f6fe0290 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:19 +0200 Subject: [PATCH 102/123] ARM: davinci: da850-evm: switch to using a fixed regulator for lcdc Now that the da8xx fbdev driver supports power control with an actual regulator, switch to using a fixed power supply for da850-evm. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 62 ++++++++++++++++++------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index ac05d4838f1e..5b3549f1236c 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -803,12 +803,6 @@ static const short da850_evm_mmcsd0_pins[] __initconst = { -1 }; -static void da850_panel_power_ctrl(int val) -{ - /* lcd power */ - gpio_set_value(DA850_LCD_PWR_PIN, val); -} - static struct property_entry da850_lcd_backlight_props[] = { PROPERTY_ENTRY_BOOL("default-on"), { } @@ -828,28 +822,61 @@ static const struct platform_device_info da850_lcd_backlight_info = { .properties = da850_lcd_backlight_props, }; +static struct regulator_consumer_supply da850_lcd_supplies[] = { + REGULATOR_SUPPLY("lcd", NULL), +}; + +static struct regulator_init_data da850_lcd_supply_data = { + .consumer_supplies = da850_lcd_supplies, + .num_consumer_supplies = ARRAY_SIZE(da850_lcd_supplies), + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, +}; + +static struct fixed_voltage_config da850_lcd_supply = { + .supply_name = "lcd", + .microvolts = 33000000, + .init_data = &da850_lcd_supply_data, +}; + +static struct platform_device da850_lcd_supply_device = { + .name = "reg-fixed-voltage", + .id = 1, /* Dummy fixed regulator is 0 */ + .dev = { + .platform_data = &da850_lcd_supply, + }, +}; + +static struct gpiod_lookup_table da850_lcd_supply_gpio_table = { + .dev_id = "reg-fixed-voltage.1", + .table = { + GPIO_LOOKUP("davinci_gpio", DA850_LCD_PWR_PIN, NULL, 0), + { } + }, +}; + +static struct gpiod_lookup_table *da850_lcd_gpio_lookups[] = { + &da850_lcd_backlight_gpio_table, + &da850_lcd_supply_gpio_table, +}; + static int da850_lcd_hw_init(void) { struct platform_device *backlight; int status; - gpiod_add_lookup_table(&da850_lcd_backlight_gpio_table); + gpiod_add_lookup_tables(da850_lcd_gpio_lookups, + ARRAY_SIZE(da850_lcd_gpio_lookups)); + backlight = platform_device_register_full(&da850_lcd_backlight_info); if (IS_ERR(backlight)) return PTR_ERR(backlight); - status = gpio_request(DA850_LCD_PWR_PIN, "lcd pwr"); - if (status < 0) + status = platform_device_register(&da850_lcd_supply_device); + if (status) return status; - gpio_direction_output(DA850_LCD_PWR_PIN, 0); - - /* Switch off panel power */ - da850_panel_power_ctrl(0); - - /* Switch on panel power */ - da850_panel_power_ctrl(1); - return 0; } @@ -1459,7 +1486,6 @@ static __init void da850_evm_init(void) if (ret) pr_warn("%s: LCD initialization failed: %d\n", __func__, ret); - sharp_lk043t1dg01_pdata.panel_power_ctrl = da850_panel_power_ctrl, ret = da8xx_register_lcdc(&sharp_lk043t1dg01_pdata); if (ret) pr_warn("%s: LCDC registration failed: %d\n", __func__, ret); From 3fca9e0be9b5b7f4ccd5f71941143d9719047a05 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:20 +0200 Subject: [PATCH 103/123] fbdev: da8xx: remove panel_power_ctrl() callback from platform data There are no more users of panel_power_ctrl(). Remove it from the driver. Signed-off-by: Bartosz Golaszewski Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sekhar Nori --- drivers/video/fbdev/da8xx-fb.c | 25 +++++-------------------- include/video/da8xx-fb.h | 1 - 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index 02dfe9e32eed..19ed9889c8f8 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -165,7 +165,6 @@ struct da8xx_fb_par { struct notifier_block freq_transition; #endif unsigned int lcdc_clk_rate; - void (*panel_power_ctrl)(int); struct regulator *lcd_supply; u32 pseudo_palette[16]; struct fb_videomode mode; @@ -1076,9 +1075,7 @@ static int fb_remove(struct platform_device *dev) #ifdef CONFIG_CPU_FREQ lcd_da8xx_cpufreq_deregister(par); #endif - if (par->panel_power_ctrl) { - par->panel_power_ctrl(0); - } else if (par->lcd_supply) { + if (par->lcd_supply) { ret = regulator_disable(par->lcd_supply); if (ret) return ret; @@ -1187,9 +1184,7 @@ static int cfb_blank(int blank, struct fb_info *info) case FB_BLANK_UNBLANK: lcd_enable_raster(); - if (par->panel_power_ctrl) { - par->panel_power_ctrl(1); - } else if (par->lcd_supply) { + if (par->lcd_supply) { ret = regulator_enable(par->lcd_supply); if (ret) return ret; @@ -1199,9 +1194,7 @@ static int cfb_blank(int blank, struct fb_info *info) case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_POWERDOWN: - if (par->panel_power_ctrl) { - par->panel_power_ctrl(0); - } else if (par->lcd_supply) { + if (par->lcd_supply) { ret = regulator_disable(par->lcd_supply); if (ret) return ret; @@ -1413,10 +1406,6 @@ static int fb_probe(struct platform_device *device) par->dev = &device->dev; par->lcdc_clk = tmp_lcdc_clk; par->lcdc_clk_rate = clk_get_rate(par->lcdc_clk); - if (fb_pdata->panel_power_ctrl) { - par->panel_power_ctrl = fb_pdata->panel_power_ctrl; - par->panel_power_ctrl(1); - } par->lcd_supply = devm_regulator_get_optional(&device->dev, "lcd"); if (IS_ERR(par->lcd_supply)) { @@ -1638,9 +1627,7 @@ static int fb_suspend(struct device *dev) int ret; console_lock(); - if (par->panel_power_ctrl) { - par->panel_power_ctrl(0); - } else if (par->lcd_supply) { + if (par->lcd_supply) { ret = regulator_disable(par->lcd_supply); if (ret) return ret; @@ -1666,9 +1653,7 @@ static int fb_resume(struct device *dev) if (par->blank == FB_BLANK_UNBLANK) { lcd_enable_raster(); - if (par->panel_power_ctrl) { - par->panel_power_ctrl(1); - } else if (par->lcd_supply) { + if (par->lcd_supply) { ret = regulator_enable(par->lcd_supply); if (ret) return ret; diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h index efed3c3383d6..1d19ae62b844 100644 --- a/include/video/da8xx-fb.h +++ b/include/video/da8xx-fb.h @@ -32,7 +32,6 @@ struct da8xx_lcdc_platform_data { const char manu_name[10]; void *controller_data; const char type[25]; - void (*panel_power_ctrl)(int); }; struct lcd_ctrl_config { From c957c88f7be109a14294289f013b03bbe94e8af5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:21 +0200 Subject: [PATCH 104/123] fbdev: da8xx-fb: use devm_platform_ioremap_resource() Shrink the code a bit by using the new helper wrapping the calls to platform_get_resource() and devm_ioremap_resource() together. Signed-off-by: Bartosz Golaszewski Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sekhar Nori --- drivers/video/fbdev/da8xx-fb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index 19ed9889c8f8..f2f66605e8fb 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -1339,7 +1339,6 @@ static int fb_probe(struct platform_device *device) { struct da8xx_lcdc_platform_data *fb_pdata = dev_get_platdata(&device->dev); - struct resource *lcdc_regs; struct lcd_ctrl_config *lcd_cfg; struct fb_videomode *lcdc_info; struct fb_info *da8xx_fb_info; @@ -1357,8 +1356,7 @@ static int fb_probe(struct platform_device *device) if (lcdc_info == NULL) return -ENODEV; - lcdc_regs = platform_get_resource(device, IORESOURCE_MEM, 0); - da8xx_fb_reg_base = devm_ioremap_resource(&device->dev, lcdc_regs); + da8xx_fb_reg_base = devm_platform_ioremap_resource(device, 0); if (IS_ERR(da8xx_fb_reg_base)) return PTR_ERR(da8xx_fb_reg_base); From 8a3665f72d77867745099178a40200dd548161e5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:22 +0200 Subject: [PATCH 105/123] fbdev: da8xx-fb: drop a redundant if The driver data is always set in probe. The remove() callback won't be called if probe failed which is the only way for it to be NULL. Remove the redundant if. Signed-off-by: Bartosz Golaszewski Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sekhar Nori --- drivers/video/fbdev/da8xx-fb.c | 49 ++++++++++++++++------------------ 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index f2f66605e8fb..d14ea6f91371 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -1067,37 +1067,34 @@ static void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par) static int fb_remove(struct platform_device *dev) { struct fb_info *info = dev_get_drvdata(&dev->dev); + struct da8xx_fb_par *par = info->par; int ret; - if (info) { - struct da8xx_fb_par *par = info->par; - #ifdef CONFIG_CPU_FREQ - lcd_da8xx_cpufreq_deregister(par); + lcd_da8xx_cpufreq_deregister(par); #endif - if (par->lcd_supply) { - ret = regulator_disable(par->lcd_supply); - if (ret) - return ret; - } - - lcd_disable_raster(DA8XX_FRAME_WAIT); - lcdc_write(0, LCD_RASTER_CTRL_REG); - - /* disable DMA */ - lcdc_write(0, LCD_DMA_CTRL_REG); - - unregister_framebuffer(info); - fb_dealloc_cmap(&info->cmap); - dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, - par->p_palette_base); - dma_free_coherent(par->dev, par->vram_size, par->vram_virt, - par->vram_phys); - pm_runtime_put_sync(&dev->dev); - pm_runtime_disable(&dev->dev); - framebuffer_release(info); - + if (par->lcd_supply) { + ret = regulator_disable(par->lcd_supply); + if (ret) + return ret; } + + lcd_disable_raster(DA8XX_FRAME_WAIT); + lcdc_write(0, LCD_RASTER_CTRL_REG); + + /* disable DMA */ + lcdc_write(0, LCD_DMA_CTRL_REG); + + unregister_framebuffer(info); + fb_dealloc_cmap(&info->cmap); + dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, + par->p_palette_base); + dma_free_coherent(par->dev, par->vram_size, par->vram_virt, + par->vram_phys); + pm_runtime_put_sync(&dev->dev); + pm_runtime_disable(&dev->dev); + framebuffer_release(info); + return 0; } From 671da5f3444b09779f108d28cc69414c57deab8c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 22 Jul 2019 15:44:23 +0200 Subject: [PATCH 106/123] fbdev: da8xx: use resource management for dma Use managed variants of dma alloc functions in the da8xx fbdev driver. Signed-off-by: Bartosz Golaszewski Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Sekhar Nori --- drivers/video/fbdev/da8xx-fb.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c index d14ea6f91371..2d3dcc52fcf3 100644 --- a/drivers/video/fbdev/da8xx-fb.c +++ b/drivers/video/fbdev/da8xx-fb.c @@ -1087,10 +1087,6 @@ static int fb_remove(struct platform_device *dev) unregister_framebuffer(info); fb_dealloc_cmap(&info->cmap); - dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, - par->p_palette_base); - dma_free_coherent(par->dev, par->vram_size, par->vram_virt, - par->vram_phys); pm_runtime_put_sync(&dev->dev); pm_runtime_disable(&dev->dev); framebuffer_release(info); @@ -1427,10 +1423,10 @@ static int fb_probe(struct platform_device *device) par->vram_size = roundup(par->vram_size/8, ulcm); par->vram_size = par->vram_size * LCD_NUM_BUFFERS; - par->vram_virt = dma_alloc_coherent(par->dev, - par->vram_size, - &par->vram_phys, - GFP_KERNEL | GFP_DMA); + par->vram_virt = dmam_alloc_coherent(par->dev, + par->vram_size, + &par->vram_phys, + GFP_KERNEL | GFP_DMA); if (!par->vram_virt) { dev_err(&device->dev, "GLCD: kmalloc for frame buffer failed\n"); @@ -1448,20 +1444,20 @@ static int fb_probe(struct platform_device *device) da8xx_fb_fix.line_length - 1; /* allocate palette buffer */ - par->v_palette_base = dma_alloc_coherent(par->dev, PALETTE_SIZE, - &par->p_palette_base, - GFP_KERNEL | GFP_DMA); + par->v_palette_base = dmam_alloc_coherent(par->dev, PALETTE_SIZE, + &par->p_palette_base, + GFP_KERNEL | GFP_DMA); if (!par->v_palette_base) { dev_err(&device->dev, "GLCD: kmalloc for palette buffer failed\n"); ret = -EINVAL; - goto err_release_fb_mem; + goto err_release_fb; } par->irq = platform_get_irq(device, 0); if (par->irq < 0) { ret = -ENOENT; - goto err_release_pl_mem; + goto err_release_fb; } da8xx_fb_var.grayscale = @@ -1479,7 +1475,7 @@ static int fb_probe(struct platform_device *device) ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); if (ret) - goto err_release_pl_mem; + goto err_release_fb; da8xx_fb_info->cmap.len = par->palette_sz; /* initialize var_screeninfo */ @@ -1533,14 +1529,6 @@ err_cpu_freq: err_dealloc_cmap: fb_dealloc_cmap(&da8xx_fb_info->cmap); -err_release_pl_mem: - dma_free_coherent(par->dev, PALETTE_SIZE, par->v_palette_base, - par->p_palette_base); - -err_release_fb_mem: - dma_free_coherent(par->dev, par->vram_size, par->vram_virt, - par->vram_phys); - err_release_fb: framebuffer_release(da8xx_fb_info); From eef3c2ba0a42a6aa709828e968b64bd11f4aeb19 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 23 Aug 2019 11:04:15 +0200 Subject: [PATCH 107/123] soc: amlogic: Add support for Everything-Else power domains controller Add support for the General Purpose Amlogic Everything-Else Power controller, with the first support for G12A and SM1 SoCs dedicated to the VPU, PCIe, USB, NNA, GE2D and Ethernet Power Domains. Signed-off-by: Neil Armstrong Reviewed-by: Kevin Hilman Tested-by: Kevin Hilman Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/Kconfig | 11 + drivers/soc/amlogic/Makefile | 1 + drivers/soc/amlogic/meson-ee-pwrc.c | 492 ++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 drivers/soc/amlogic/meson-ee-pwrc.c diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig index 23bfb8ef4fdb..bc2c912949bd 100644 --- a/drivers/soc/amlogic/Kconfig +++ b/drivers/soc/amlogic/Kconfig @@ -37,6 +37,17 @@ config MESON_GX_PM_DOMAINS Say yes to expose Amlogic Meson GX Power Domains as Generic Power Domains. +config MESON_EE_PM_DOMAINS + bool "Amlogic Meson Everything-Else Power Domains driver" + depends on ARCH_MESON || COMPILE_TEST + depends on PM && OF + default ARCH_MESON + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + Say yes to expose Amlogic Meson Everything-Else Power Domains as + Generic Power Domains. + config MESON_MX_SOCINFO bool "Amlogic Meson MX SoC Information driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile index f2e4ed171297..de79d044b545 100644 --- a/drivers/soc/amlogic/Makefile +++ b/drivers/soc/amlogic/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MESON_CLK_MEASURE) += meson-clk-measure.o obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o +obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o diff --git a/drivers/soc/amlogic/meson-ee-pwrc.c b/drivers/soc/amlogic/meson-ee-pwrc.c new file mode 100644 index 000000000000..5823f5b67d16 --- /dev/null +++ b/drivers/soc/amlogic/meson-ee-pwrc.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) +#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) +#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) +#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) +#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +struct meson_ee_pwrc; +struct meson_ee_pwrc_domain; + +struct meson_ee_pwrc_mem_domain { + unsigned int reg; + unsigned int mask; +}; + +struct meson_ee_pwrc_top_domain { + unsigned int sleep_reg; + unsigned int sleep_mask; + unsigned int iso_reg; + unsigned int iso_mask; +}; + +struct meson_ee_pwrc_domain_desc { + char *name; + unsigned int reset_names_count; + unsigned int clk_names_count; + struct meson_ee_pwrc_top_domain *top_pd; + unsigned int mem_pd_count; + struct meson_ee_pwrc_mem_domain *mem_pd; + bool (*get_power)(struct meson_ee_pwrc_domain *pwrc_domain); +}; + +struct meson_ee_pwrc_domain_data { + unsigned int count; + struct meson_ee_pwrc_domain_desc *domains; +}; + +/* TOP Power Domains */ + +static struct meson_ee_pwrc_top_domain g12a_pwrc_vpu = { + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +#define SM1_EE_PD(__bit) \ + { \ + .sleep_reg = AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_mask = BIT(__bit), \ + .iso_reg = AO_RTI_GEN_PWR_ISO0, \ + .iso_mask = BIT(__bit), \ + } + +static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); + +/* Memory PD Domains */ + +#define VPU_MEMPD(__reg) \ + { __reg, GENMASK(1, 0) }, \ + { __reg, GENMASK(3, 2) }, \ + { __reg, GENMASK(5, 4) }, \ + { __reg, GENMASK(7, 6) }, \ + { __reg, GENMASK(9, 8) }, \ + { __reg, GENMASK(11, 10) }, \ + { __reg, GENMASK(13, 12) }, \ + { __reg, GENMASK(15, 14) }, \ + { __reg, GENMASK(17, 16) }, \ + { __reg, GENMASK(19, 18) }, \ + { __reg, GENMASK(21, 20) }, \ + { __reg, GENMASK(23, 22) }, \ + { __reg, GENMASK(25, 24) }, \ + { __reg, GENMASK(27, 26) }, \ + { __reg, GENMASK(29, 28) }, \ + { __reg, GENMASK(31, 30) } + +#define VPU_HHI_MEMPD(__reg) \ + { __reg, BIT(8) }, \ + { __reg, BIT(9) }, \ + { __reg, BIT(10) }, \ + { __reg, BIT(11) }, \ + { __reg, BIT(12) }, \ + { __reg, BIT(13) }, \ + { __reg, BIT(14) }, \ + { __reg, BIT(15) } + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_eth[] = { + { HHI_MEM_PD_REG0, GENMASK(3, 2) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_MEMPD(HHI_VPU_MEM_PD_REG3), + { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { + { HHI_NANOQ_MEM_PD_REG0, 0xff }, + { HHI_NANOQ_MEM_PD_REG1, 0xff }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { + { HHI_MEM_PD_REG0, GENMASK(31, 30) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { + { HHI_MEM_PD_REG0, GENMASK(29, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { + { HHI_MEM_PD_REG0, GENMASK(25, 18) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, +}; + +#define VPU_PD(__name, __top_pd, __mem, __get_power, __resets, __clks) \ + { \ + .name = __name, \ + .reset_names_count = __resets, \ + .clk_names_count = __clks, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define TOP_PD(__name, __top_pd, __mem, __get_power) \ + { \ + .name = __name, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .get_power = __get_power, \ + } + +#define MEM_PD(__name, __mem) \ + TOP_PD(__name, NULL, __mem, NULL) + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain); + +static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &g12a_pwrc_vpu, g12a_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { + [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, + pwrc_ee_get_power, 11, 2), + [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, + pwrc_ee_get_power), + [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, + pwrc_ee_get_power), + [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, + pwrc_ee_get_power), + [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, + pwrc_ee_get_power), + [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", g12a_pwrc_mem_eth), +}; + +struct meson_ee_pwrc_domain { + struct generic_pm_domain base; + bool enabled; + struct meson_ee_pwrc *pwrc; + struct meson_ee_pwrc_domain_desc desc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *rstc; + int num_rstc; +}; + +struct meson_ee_pwrc { + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct meson_ee_pwrc_domain *domains; + struct genpd_onecell_data xlate; +}; + +static bool pwrc_ee_get_power(struct meson_ee_pwrc_domain *pwrc_domain) +{ + u32 reg; + + regmap_read(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, ®); + + return (reg & pwrc_domain->desc.top_pd->sleep_mask); +} + +static int meson_ee_pwrc_off(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, + pwrc_domain->desc.top_pd->sleep_mask); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, + pwrc_domain->desc.mem_pd[i].mask); + + udelay(20); + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, + pwrc_domain->desc.top_pd->iso_mask); + + if (pwrc_domain->num_clks) { + msleep(20); + clk_bulk_disable_unprepare(pwrc_domain->num_clks, + pwrc_domain->clks); + } + + return 0; +} + +static int meson_ee_pwrc_on(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i, ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, 0); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, 0); + + udelay(20); + + ret = reset_control_assert(pwrc_domain->rstc); + if (ret) + return ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, 0); + + ret = reset_control_deassert(pwrc_domain->rstc); + if (ret) + return ret; + + return clk_bulk_prepare_enable(pwrc_domain->num_clks, + pwrc_domain->clks); +} + +static int meson_ee_pwrc_init_domain(struct platform_device *pdev, + struct meson_ee_pwrc *pwrc, + struct meson_ee_pwrc_domain *dom) +{ + dom->pwrc = pwrc; + dom->num_rstc = dom->desc.reset_names_count; + dom->num_clks = dom->desc.clk_names_count; + + if (dom->num_rstc) { + int count = reset_control_get_count(&pdev->dev); + + if (count != dom->num_rstc) + dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n", + count, dom->desc.name); + + dom->rstc = devm_reset_control_array_get(&pdev->dev, false, + false); + if (IS_ERR(dom->rstc)) + return PTR_ERR(dom->rstc); + } + + if (dom->num_clks) { + int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks); + if (ret < 0) + return ret; + + if (dom->num_clks != ret) { + dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n", + ret, dom->desc.name); + dom->num_clks = ret; + } + } + + dom->base.name = dom->desc.name; + dom->base.power_on = meson_ee_pwrc_on; + dom->base.power_off = meson_ee_pwrc_off; + + /* + * TOFIX: This is a special case for the VPU power domain, which can + * be enabled previously by the bootloader. In this case the VPU + * pipeline may be functional but no driver maybe never attach + * to this power domain, and if the domain is disabled it could + * cause system errors. This is why the pm_domain_always_on_gov + * is used here. + * For the same reason, the clocks should be enabled in case + * we need to power the domain off, otherwise the internal clocks + * prepare/enable counters won't be in sync. + */ + if (dom->num_clks && dom->desc.get_power && !dom->desc.get_power(dom)) { + int ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks); + if (ret) + return ret; + + pm_genpd_init(&dom->base, &pm_domain_always_on_gov, false); + } else + pm_genpd_init(&dom->base, NULL, + (dom->desc.get_power ? + dom->desc.get_power(dom) : true)); + + return 0; +} + +static int meson_ee_pwrc_probe(struct platform_device *pdev) +{ + const struct meson_ee_pwrc_domain_data *match; + struct regmap *regmap_ao, *regmap_hhi; + struct meson_ee_pwrc *pwrc; + int i, ret; + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) + return -ENOMEM; + + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->xlate.domains), + GFP_KERNEL); + if (!pwrc->xlate.domains) + return -ENOMEM; + + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->domains), GFP_KERNEL); + if (!pwrc->domains) + return -ENOMEM; + + pwrc->xlate.num_domains = match->count; + + regmap_hhi = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-sysctrl"); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get AO regmap\n"); + return PTR_ERR(regmap_ao); + } + + pwrc->regmap_ao = regmap_ao; + pwrc->regmap_hhi = regmap_hhi; + + platform_set_drvdata(pdev, pwrc); + + for (i = 0 ; i < match->count ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc)); + + ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom); + if (ret) + return ret; + + pwrc->xlate.domains[i] = &dom->base; + } + + of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); + + return 0; +} + +static void meson_ee_pwrc_shutdown(struct platform_device *pdev) +{ + struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev); + int i; + + for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + if (dom->desc.get_power && !dom->desc.get_power(dom)) + meson_ee_pwrc_off(&dom->base); + } +} + +static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { + .count = ARRAY_SIZE(g12a_pwrc_domains), + .domains = g12a_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { + .count = ARRAY_SIZE(sm1_pwrc_domains), + .domains = sm1_pwrc_domains, +}; + +static const struct of_device_id meson_ee_pwrc_match_table[] = { + { + .compatible = "amlogic,meson-g12a-pwrc", + .data = &meson_ee_g12a_pwrc_data, + }, + { + .compatible = "amlogic,meson-sm1-pwrc", + .data = &meson_ee_sm1_pwrc_data, + }, + { /* sentinel */ } +}; + +static struct platform_driver meson_ee_pwrc_driver = { + .probe = meson_ee_pwrc_probe, + .shutdown = meson_ee_pwrc_shutdown, + .driver = { + .name = "meson_ee_pwrc", + .of_match_table = meson_ee_pwrc_match_table, + }, +}; +builtin_platform_driver(meson_ee_pwrc_driver); From bd9eccf14008732f8ea4a7efc7839911e56053a1 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Fri, 23 Aug 2019 11:04:14 +0200 Subject: [PATCH 108/123] dt-bindings: power: add Amlogic Everything-Else power domains bindings Add the bindings for the Amlogic Everything-Else power domains, controlling the Everything-Else peripherals power domains. The bindings targets the Amlogic G12A and SM1 compatible SoCs, support for earlier SoCs will be added later. Signed-off-by: Neil Armstrong Reviewed-by: Rob Herring Signed-off-by: Kevin Hilman --- .../bindings/power/amlogic,meson-ee-pwrc.yaml | 93 +++++++++++++++++++ include/dt-bindings/power/meson-g12a-power.h | 13 +++ include/dt-bindings/power/meson-sm1-power.h | 18 ++++ 3 files changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml create mode 100644 include/dt-bindings/power/meson-g12a-power.h create mode 100644 include/dt-bindings/power/meson-sm1-power.h diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml new file mode 100644 index 000000000000..aab70e8b681e --- /dev/null +++ b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2019 BayLibre, SAS +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/amlogic,meson-ee-pwrc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic Meson Everything-Else Power Domains + +maintainers: + - Neil Armstrong + +description: |+ + The Everything-Else Power Domains node should be the child of a syscon + node with the required property: + + - compatible: Should be the following: + "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon" + + Refer to the the bindings described in + Documentation/devicetree/bindings/mfd/syscon.txt + +properties: + compatible: + enum: + - amlogic,meson-g12a-pwrc + - amlogic,meson-sm1-pwrc + + clocks: + minItems: 2 + + clock-names: + items: + - const: vpu + - const: vapb + + resets: + minItems: 11 + + reset-names: + items: + - const: viu + - const: venc + - const: vcbus + - const: bt656 + - const: rdma + - const: venci + - const: vencp + - const: vdac + - const: vdi6 + - const: vencl + - const: vid_lock + + "#power-domain-cells": + const: 1 + + amlogic,ao-sysctrl: + description: phandle to the AO sysctrl node + allOf: + - $ref: /schemas/types.yaml#/definitions/phandle + +required: + - compatible + - clocks + - clock-names + - resets + - reset-names + - "#power-domain-cells" + - amlogic,ao-sysctrl + +examples: + - | + pwrc: power-controller { + compatible = "amlogic,meson-sm1-pwrc"; + #power-domain-cells = <1>; + amlogic,ao-sysctrl = <&rti>; + resets = <&reset_viu>, + <&reset_venc>, + <&reset_vcbus>, + <&reset_bt656>, + <&reset_rdma>, + <&reset_venci>, + <&reset_vencp>, + <&reset_vdac>, + <&reset_vdi6>, + <&reset_vencl>, + <&reset_vid_lock>; + reset-names = "viu", "venc", "vcbus", "bt656", + "rdma", "venci", "vencp", "vdac", + "vdi6", "vencl", "vid_lock"; + clocks = <&clk_vpu>, <&clk_vapb>; + clock-names = "vpu", "vapb"; + }; diff --git a/include/dt-bindings/power/meson-g12a-power.h b/include/dt-bindings/power/meson-g12a-power.h new file mode 100644 index 000000000000..bb5e67a842de --- /dev/null +++ b/include/dt-bindings/power/meson-g12a-power.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef _DT_BINDINGS_MESON_G12A_POWER_H +#define _DT_BINDINGS_MESON_G12A_POWER_H + +#define PWRC_G12A_VPU_ID 0 +#define PWRC_G12A_ETH_ID 1 + +#endif diff --git a/include/dt-bindings/power/meson-sm1-power.h b/include/dt-bindings/power/meson-sm1-power.h new file mode 100644 index 000000000000..a020ab00c134 --- /dev/null +++ b/include/dt-bindings/power/meson-sm1-power.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#ifndef _DT_BINDINGS_MESON_SM1_POWER_H +#define _DT_BINDINGS_MESON_SM1_POWER_H + +#define PWRC_SM1_VPU_ID 0 +#define PWRC_SM1_NNA_ID 1 +#define PWRC_SM1_USB_ID 2 +#define PWRC_SM1_PCIE_ID 3 +#define PWRC_SM1_GE2D_ID 4 +#define PWRC_SM1_AUDIO_ID 5 +#define PWRC_SM1_ETH_ID 6 + +#endif From f73d137d562f4c8bf225083c96bc7a91354f06cc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Aug 2019 12:34:29 +0200 Subject: [PATCH 109/123] ARM: scoop: Use the right include This is a GPIO driver so it should include not Link: https://lore.kernel.org/r/20190820103429.7028-1-linus.walleij@linaro.org Cc: Richard Purdie Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann --- arch/arm/common/scoop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c index 60130bd7b182..6edb961bd6c1 100644 --- a/arch/arm/common/scoop.c +++ b/arch/arm/common/scoop.c @@ -8,7 +8,7 @@ */ #include -#include +#include #include #include #include From caebdd3c114e522b5c9bda2098990e09abfba5b0 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Thu, 22 Aug 2019 03:03:51 +0200 Subject: [PATCH 110/123] bus: moxtet: remove set but not used variable 'dummy' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes gcc '-Wunused-but-set-variable' warning: drivers/bus/moxtet.c: In function moxtet_remove: drivers/bus/moxtet.c:822:6: warning: variable dummy set but not used [-Wunused-but-set-variable] Link: https://lore.kernel.org/r/20190822010351.15660-3-marek.behun@nic.cz Signed-off-by: Jason Yan Signed-off-by: Marek Behún Signed-off-by: Arnd Bergmann --- drivers/bus/moxtet.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c index 1ee4570e7e17..baccf7609357 100644 --- a/drivers/bus/moxtet.c +++ b/drivers/bus/moxtet.c @@ -819,7 +819,6 @@ static int moxtet_probe(struct spi_device *spi) static int moxtet_remove(struct spi_device *spi) { struct moxtet *moxtet = spi_get_drvdata(spi); - int dummy; free_irq(moxtet->dev_irq, moxtet); @@ -827,7 +826,7 @@ static int moxtet_remove(struct spi_device *spi) moxtet_unregister_debugfs(moxtet); - dummy = device_for_each_child(moxtet->dev, NULL, __unregister); + device_for_each_child(moxtet->dev, NULL, __unregister); mutex_destroy(&moxtet->lock); From 6811d26df50d96635dd339cf8cdf43a6abc0c4b6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 22 Aug 2019 03:03:50 +0200 Subject: [PATCH 111/123] bus: moxtet: fix unsigned comparison to less than zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the size_t variable res is being checked for an error failure however the unsigned variable is never less than zero so this test is always false. Fix this by making variable res ssize_t Link: https://lore.kernel.org/r/20190822010351.15660-2-marek.behun@nic.cz Addresses-Coverity: ("Unsigned compared against 0") Fixes: 5bc7f990cd98 ("bus: Add support for Moxtet bus") Signed-off-by: Colin Ian King Signed-off-by: Marek Behún Signed-off-by: Arnd Bergmann --- drivers/bus/moxtet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c index baccf7609357..36cf13eee6b8 100644 --- a/drivers/bus/moxtet.c +++ b/drivers/bus/moxtet.c @@ -514,7 +514,7 @@ static ssize_t output_write(struct file *file, const char __user *buf, struct moxtet *moxtet = file->private_data; u8 bin[TURRIS_MOX_MAX_MODULES]; u8 hex[sizeof(bin) * 2 + 1]; - size_t res; + ssize_t res; loff_t dummy = 0; int err, i; From 2c4aaa8763c51e2342cf5337d84b449db4a8ad63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 22 Aug 2019 03:43:16 +0200 Subject: [PATCH 112/123] dt-bindings: firmware: Document cznic,turris-mox-rwtm binding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds device tree binding documentation for the driver communicating with the rWTM firmware on Turris Mox. Link: https://lore.kernel.org/r/20190822014318.19478-2-marek.behun@nic.cz Signed-off-by: Marek Behún Reviewed-by: Rob Herring Signed-off-by: Arnd Bergmann --- .../firmware/cznic,turris-mox-rwtm.txt | 19 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt diff --git a/Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt b/Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt new file mode 100644 index 000000000000..338169dea7bb --- /dev/null +++ b/Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt @@ -0,0 +1,19 @@ +Turris Mox rWTM firmware driver + +Required properties: + - compatible : Should be "cznic,turris-mox-rwtm" + - mboxes : Must contain a reference to associated mailbox + +This device tree node should be used on Turris Mox, or potentially another A3700 +compatible device running the Mox's rWTM firmware in the secure processor (for +example it is possible to flash this firmware into EspressoBin). + +Example: + + firmware { + turris-mox-rwtm { + compatible = "cznic,turris-mox-rwtm"; + mboxes = <&rwtm 0>; + status = "okay"; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index bd95faf18f40..1cf01240385a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1633,6 +1633,7 @@ S: Maintained F: Documentation/ABI/testing/debugfs-moxtet F: Documentation/ABI/testing/sysfs-bus-moxtet-devices F: Documentation/devicetree/bindings/bus/moxtet.txt +F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c From 389711b374939968d2d0adede01b9e1f3bb4f531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 22 Aug 2019 03:43:17 +0200 Subject: [PATCH 113/123] firmware: Add Turris Mox rWTM firmware driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a driver to communicate with the firmware running on the secure processor of the Turris Mox router, enabling the kernel to retrieve true random numbers from the Entropy Bit Generator and to read some information burned into eFuses when device was manufactured: and to sign messages with the ECDSA private key burned into each Turris Mox device when manufacturing. This also adds support to read other information burned into eFuses: - serial number - board version - MAC addresses - RAM size - ECDSA public key (this is not read directly from eFuses, rather it is computed by the firmware as pair to the burned private key) The source code of the firmware is open source and can be found at https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi The firmware is also able to, on demand, sign messages with the burned ECDSA private key, but since Linux's akcipher API is not yet stable (and therefore not exposed to userspace via netlink), this functionality is not supported yet. Link: https://lore.kernel.org/r/20190822014318.19478-3-marek.behun@nic.cz Signed-off-by: Marek Behún Signed-off-by: Arnd Bergmann --- MAINTAINERS | 1 + drivers/firmware/Kconfig | 14 ++ drivers/firmware/Makefile | 1 + drivers/firmware/turris-mox-rwtm.c | 384 +++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 drivers/firmware/turris-mox-rwtm.c diff --git a/MAINTAINERS b/MAINTAINERS index 1cf01240385a..cc2c5c99a5fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1637,6 +1637,7 @@ F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt F: include/linux/moxtet.h F: drivers/bus/moxtet.c +F: drivers/firmware/turris-mox-rwtm.c F: drivers/gpio/gpio-moxtet.c ARM/EBSA110 MACHINE SUPPORT diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 53446e39a32c..d2f466fdcd70 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -270,6 +270,20 @@ config TRUSTED_FOUNDATIONS Choose N if you don't know what this is about. +config TURRIS_MOX_RWTM + tristate "Turris Mox rWTM secure firmware driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on HAS_DMA && OF + depends on MAILBOX + select HW_RANDOM + select ARMADA_37XX_RWTM_MBOX + help + This driver communicates with the firmware on the Cortex-M3 secure + processor of the Turris Mox router. Enable if you are building for + Turris Mox, and you will be able to read the device serial number and + other manufacturing data and also utilize the Entropy Bit Generator + for hardware random number generation. + config HAVE_ARM_SMCCC bool diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3fa0b34eb72f..2b6e3a0be595 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o +obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ obj-y += psci/ diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c new file mode 100644 index 000000000000..72be58960e54 --- /dev/null +++ b/drivers/firmware/turris-mox-rwtm.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Turris Mox rWTM firmware driver + * + * Copyright (C) 2019 Marek Behun + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "turris-mox-rwtm" + +/* + * The macros and constants below come from Turris Mox's rWTM firmware code. + * This firmware is open source and it's sources can be found at + * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. + */ + +#define MBOX_STS_SUCCESS (0 << 30) +#define MBOX_STS_FAIL (1 << 30) +#define MBOX_STS_BADCMD (2 << 30) +#define MBOX_STS_ERROR(s) ((s) & (3 << 30)) +#define MBOX_STS_VALUE(s) (((s) >> 10) & 0xfffff) +#define MBOX_STS_CMD(s) ((s) & 0x3ff) + +enum mbox_cmd { + MBOX_CMD_GET_RANDOM = 1, + MBOX_CMD_BOARD_INFO = 2, + MBOX_CMD_ECDSA_PUB_KEY = 3, + MBOX_CMD_HASH = 4, + MBOX_CMD_SIGN = 5, + MBOX_CMD_VERIFY = 6, + + MBOX_CMD_OTP_READ = 7, + MBOX_CMD_OTP_WRITE = 8, +}; + +struct mox_kobject; + +struct mox_rwtm { + struct device *dev; + struct mbox_client mbox_client; + struct mbox_chan *mbox; + struct mox_kobject *kobj; + struct hwrng hwrng; + + struct armada_37xx_rwtm_rx_msg reply; + + void *buf; + dma_addr_t buf_phys; + + struct mutex busy; + struct completion cmd_done; + + /* board information */ + int has_board_info; + u64 serial_number; + int board_version, ram_size; + u8 mac_address1[6], mac_address2[6]; + + /* public key burned in eFuse */ + int has_pubkey; + u8 pubkey[135]; +}; + +struct mox_kobject { + struct kobject kobj; + struct mox_rwtm *rwtm; +}; + +static inline struct kobject *rwtm_to_kobj(struct mox_rwtm *rwtm) +{ + return &rwtm->kobj->kobj; +} + +static inline struct mox_rwtm *to_rwtm(struct kobject *kobj) +{ + return container_of(kobj, struct mox_kobject, kobj)->rwtm; +} + +static void mox_kobj_release(struct kobject *kobj) +{ + kfree(to_rwtm(kobj)->kobj); +} + +static struct kobj_type mox_kobj_ktype = { + .release = mox_kobj_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static int mox_kobj_create(struct mox_rwtm *rwtm) +{ + rwtm->kobj = kzalloc(sizeof(*rwtm->kobj), GFP_KERNEL); + if (!rwtm->kobj) + return -ENOMEM; + + kobject_init(rwtm_to_kobj(rwtm), &mox_kobj_ktype); + if (kobject_add(rwtm_to_kobj(rwtm), firmware_kobj, "turris-mox-rwtm")) { + kobject_put(rwtm_to_kobj(rwtm)); + return -ENXIO; + } + + rwtm->kobj->rwtm = rwtm; + + return 0; +} + +#define MOX_ATTR_RO(name, format, cat) \ +static ssize_t \ +name##_show(struct kobject *kobj, struct kobj_attribute *a, \ + char *buf) \ +{ \ + struct mox_rwtm *rwtm = to_rwtm(kobj); \ + if (!rwtm->has_##cat) \ + return -ENODATA; \ + return sprintf(buf, format, rwtm->name); \ +} \ +static struct kobj_attribute mox_attr_##name = __ATTR_RO(name) + +MOX_ATTR_RO(serial_number, "%016llX\n", board_info); +MOX_ATTR_RO(board_version, "%i\n", board_info); +MOX_ATTR_RO(ram_size, "%i\n", board_info); +MOX_ATTR_RO(mac_address1, "%pM\n", board_info); +MOX_ATTR_RO(mac_address2, "%pM\n", board_info); +MOX_ATTR_RO(pubkey, "%s\n", pubkey); + +static int mox_get_status(enum mbox_cmd cmd, u32 retval) +{ + if (MBOX_STS_CMD(retval) != cmd || + MBOX_STS_ERROR(retval) != MBOX_STS_SUCCESS) + return -EIO; + else if (MBOX_STS_ERROR(retval) == MBOX_STS_FAIL) + return -(int)MBOX_STS_VALUE(retval); + else + return MBOX_STS_VALUE(retval); +} + +static const struct attribute *mox_rwtm_attrs[] = { + &mox_attr_serial_number.attr, + &mox_attr_board_version.attr, + &mox_attr_ram_size.attr, + &mox_attr_mac_address1.attr, + &mox_attr_mac_address2.attr, + &mox_attr_pubkey.attr, + NULL +}; + +static void mox_rwtm_rx_callback(struct mbox_client *cl, void *data) +{ + struct mox_rwtm *rwtm = dev_get_drvdata(cl->dev); + struct armada_37xx_rwtm_rx_msg *msg = data; + + rwtm->reply = *msg; + complete(&rwtm->cmd_done); +} + +static void reply_to_mac_addr(u8 *mac, u32 t1, u32 t2) +{ + mac[0] = t1 >> 8; + mac[1] = t1; + mac[2] = t2 >> 24; + mac[3] = t2 >> 16; + mac[4] = t2 >> 8; + mac[5] = t2; +} + +static int mox_get_board_info(struct mox_rwtm *rwtm) +{ + struct armada_37xx_rwtm_tx_msg msg; + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; + int ret; + + msg.command = MBOX_CMD_BOARD_INFO; + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2); + if (ret < 0) + return ret; + + ret = mox_get_status(MBOX_CMD_BOARD_INFO, reply->retval); + if (ret < 0 && ret != -ENODATA) { + return ret; + } else if (ret == -ENODATA) { + dev_warn(rwtm->dev, + "Board does not have manufacturing information burned!\n"); + } else { + rwtm->serial_number = reply->status[1]; + rwtm->serial_number <<= 32; + rwtm->serial_number |= reply->status[0]; + rwtm->board_version = reply->status[2]; + rwtm->ram_size = reply->status[3]; + reply_to_mac_addr(rwtm->mac_address1, reply->status[4], + reply->status[5]); + reply_to_mac_addr(rwtm->mac_address2, reply->status[6], + reply->status[7]); + rwtm->has_board_info = 1; + + pr_info("Turris Mox serial number %016llX\n", + rwtm->serial_number); + pr_info(" board version %i\n", rwtm->board_version); + pr_info(" burned RAM size %i MiB\n", rwtm->ram_size); + } + + msg.command = MBOX_CMD_ECDSA_PUB_KEY; + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&rwtm->cmd_done, HZ / 2); + if (ret < 0) + return ret; + + ret = mox_get_status(MBOX_CMD_ECDSA_PUB_KEY, reply->retval); + if (ret < 0 && ret != -ENODATA) { + return ret; + } else if (ret == -ENODATA) { + dev_warn(rwtm->dev, "Board has no public key burned!\n"); + } else { + u32 *s = reply->status; + + rwtm->has_pubkey = 1; + sprintf(rwtm->pubkey, + "%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", + ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], + s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); + } + + return 0; +} + +static int mox_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct mox_rwtm *rwtm = (struct mox_rwtm *) rng->priv; + struct armada_37xx_rwtm_tx_msg msg; + int ret; + + if (max > 4096) + max = 4096; + + msg.command = MBOX_CMD_GET_RANDOM; + msg.args[0] = 1; + msg.args[1] = rwtm->buf_phys; + msg.args[2] = (max + 3) & ~3; + + if (!wait) { + if (!mutex_trylock(&rwtm->busy)) + return -EBUSY; + } else { + mutex_lock(&rwtm->busy); + } + + ret = mbox_send_message(rwtm->mbox, &msg); + if (ret < 0) + goto unlock_mutex; + + ret = wait_for_completion_interruptible(&rwtm->cmd_done); + if (ret < 0) + goto unlock_mutex; + + ret = mox_get_status(MBOX_CMD_GET_RANDOM, rwtm->reply.retval); + if (ret < 0) + goto unlock_mutex; + + memcpy(data, rwtm->buf, max); + ret = max; + +unlock_mutex: + mutex_unlock(&rwtm->busy); + return ret; +} + +static int turris_mox_rwtm_probe(struct platform_device *pdev) +{ + struct mox_rwtm *rwtm; + struct device *dev = &pdev->dev; + int ret; + + rwtm = devm_kzalloc(dev, sizeof(*rwtm), GFP_KERNEL); + if (!rwtm) + return -ENOMEM; + + rwtm->dev = dev; + rwtm->buf = dmam_alloc_coherent(dev, PAGE_SIZE, &rwtm->buf_phys, + GFP_KERNEL); + if (!rwtm->buf) + return -ENOMEM; + + ret = mox_kobj_create(rwtm); + if (ret < 0) { + dev_err(dev, "Cannot create turris-mox-rwtm kobject!\n"); + return ret; + } + + ret = sysfs_create_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); + if (ret < 0) { + dev_err(dev, "Cannot create sysfs files!\n"); + goto put_kobj; + } + + platform_set_drvdata(pdev, rwtm); + + mutex_init(&rwtm->busy); + + rwtm->mbox_client.dev = dev; + rwtm->mbox_client.rx_callback = mox_rwtm_rx_callback; + + rwtm->mbox = mbox_request_channel(&rwtm->mbox_client, 0); + if (IS_ERR(rwtm->mbox)) { + ret = PTR_ERR(rwtm->mbox); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Cannot request mailbox channel: %i\n", + ret); + goto remove_files; + } + + init_completion(&rwtm->cmd_done); + + ret = mox_get_board_info(rwtm); + if (ret < 0) + dev_warn(dev, "Cannot read board information: %i\n", ret); + + rwtm->hwrng.name = DRIVER_NAME "_hwrng"; + rwtm->hwrng.read = mox_hwrng_read; + rwtm->hwrng.priv = (unsigned long) rwtm; + rwtm->hwrng.quality = 1024; + + ret = devm_hwrng_register(dev, &rwtm->hwrng); + if (ret < 0) { + dev_err(dev, "Cannot register HWRNG: %i\n", ret); + goto free_channel; + } + + return 0; + +free_channel: + mbox_free_channel(rwtm->mbox); +remove_files: + sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); +put_kobj: + kobject_put(rwtm_to_kobj(rwtm)); + return ret; +} + +static int turris_mox_rwtm_remove(struct platform_device *pdev) +{ + struct mox_rwtm *rwtm = platform_get_drvdata(pdev); + + sysfs_remove_files(rwtm_to_kobj(rwtm), mox_rwtm_attrs); + kobject_put(rwtm_to_kobj(rwtm)); + mbox_free_channel(rwtm->mbox); + + return 0; +} + +static const struct of_device_id turris_mox_rwtm_match[] = { + { .compatible = "cznic,turris-mox-rwtm", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, turris_mox_rwtm_match); + +static struct platform_driver turris_mox_rwtm_driver = { + .probe = turris_mox_rwtm_probe, + .remove = turris_mox_rwtm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = turris_mox_rwtm_match, + }, +}; +module_platform_driver(turris_mox_rwtm_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Turris Mox rWTM firmware driver"); +MODULE_AUTHOR("Marek Behun "); From 7db93df1c8475e7c4c3cf14411f3d3db89e10e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Thu, 22 Aug 2019 03:43:18 +0200 Subject: [PATCH 114/123] firmware: turris-mox-rwtm: Add sysfs documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sysfs ABI documentation for the sysfs files created by the turris-mox-rwtm driver. Link: https://lore.kernel.org/r/20190822014318.19478-4-marek.behun@nic.cz Signed-off-by: Marek Behún Signed-off-by: Arnd Bergmann --- .../testing/sysfs-firmware-turris-mox-rwtm | 37 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 38 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm diff --git a/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm b/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm new file mode 100644 index 000000000000..15595fab88d1 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm @@ -0,0 +1,37 @@ +What: /sys/firmware/turris-mox-rwtm/board_version +Date: August 2019 +KernelVersion: 5.4 +Contact: Marek Behún +Description: (R) Board version burned into eFuses of this Turris Mox board. + Format: %i + +What: /sys/firmware/turris-mox-rwtm/mac_address* +Date: August 2019 +KernelVersion: 5.4 +Contact: Marek Behún +Description: (R) MAC addresses burned into eFuses of this Turris Mox board. + Format: %pM + +What: /sys/firmware/turris-mox-rwtm/pubkey +Date: August 2019 +KernelVersion: 5.4 +Contact: Marek Behún +Description: (R) ECDSA public key (in pubkey hex compressed form) computed + as pair to the ECDSA private key burned into eFuses of this + Turris Mox Board. + Format: string + +What: /sys/firmware/turris-mox-rwtm/ram_size +Date: August 2019 +KernelVersion: 5.4 +Contact: Marek Behún +Description: (R) RAM size in MiB of this Turris Mox board as was detected + during manufacturing and burned into eFuses. Can be 512 or 1024. + Format: %i + +What: /sys/firmware/turris-mox-rwtm/serial_number +Date: August 2019 +KernelVersion: 5.4 +Contact: Marek Behún +Description: (R) Serial number burned into eFuses of this Turris Mox device. + Format: %016X diff --git a/MAINTAINERS b/MAINTAINERS index cc2c5c99a5fd..ad386626c9fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1632,6 +1632,7 @@ W: http://mox.turris.cz S: Maintained F: Documentation/ABI/testing/debugfs-moxtet F: Documentation/ABI/testing/sysfs-bus-moxtet-devices +F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm F: Documentation/devicetree/bindings/bus/moxtet.txt F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt F: Documentation/devicetree/bindings/gpio/gpio-moxtet.txt From 13b0aefee10dff00d36e8324dfffeee99d9db137 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:33 +0200 Subject: [PATCH 115/123] net: remove ks8695 driver The platform is getting removed, so there are no remaining users of this driver. Link: https://lore.kernel.org/r/20190809202749.742267-6-arnd@arndb.de Signed-off-by: Arnd Bergmann --- drivers/net/ethernet/micrel/Kconfig | 11 +- drivers/net/ethernet/micrel/Makefile | 1 - drivers/net/ethernet/micrel/ks8695net.c | 1632 ----------------------- drivers/net/ethernet/micrel/ks8695net.h | 108 -- 4 files changed, 1 insertion(+), 1751 deletions(-) delete mode 100644 drivers/net/ethernet/micrel/ks8695net.c delete mode 100644 drivers/net/ethernet/micrel/ks8695net.h diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig index 90a8c6bead56..b9c4d48e28e4 100644 --- a/drivers/net/ethernet/micrel/Kconfig +++ b/drivers/net/ethernet/micrel/Kconfig @@ -6,8 +6,7 @@ config NET_VENDOR_MICREL bool "Micrel devices" default y - depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM || \ - (ARM && ARCH_KS8695) + depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM ---help--- If you have a network (Ethernet) card belonging to this class, say Y. @@ -18,14 +17,6 @@ config NET_VENDOR_MICREL if NET_VENDOR_MICREL -config ARM_KS8695_ETHER - tristate "KS8695 Ethernet support" - depends on ARM && ARCH_KS8695 - select MII - ---help--- - If you wish to compile a kernel for the KS8695 and want to - use the internal ethernet then you should answer Y to this. - config KS8842 tristate "Micrel KSZ8841/42 with generic bus interface" depends on HAS_IOMEM && DMA_ENGINE diff --git a/drivers/net/ethernet/micrel/Makefile b/drivers/net/ethernet/micrel/Makefile index 848fc1c5a5dc..6d8ac5527aef 100644 --- a/drivers/net/ethernet/micrel/Makefile +++ b/drivers/net/ethernet/micrel/Makefile @@ -3,7 +3,6 @@ # Makefile for the Micrel network device drivers. # -obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o obj-$(CONFIG_KS8842) += ks8842.o obj-$(CONFIG_KS8851) += ks8851.o obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c deleted file mode 100644 index 1390ef5323a2..000000000000 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ /dev/null @@ -1,1632 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Micrel KS8695 (Centaur) Ethernet. - * - * Copyright 2008 Simtec Electronics - * Daniel Silverstone - * Vincent Sanders - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "ks8695net.h" - -#define MODULENAME "ks8695_ether" -#define MODULEVERSION "1.02" - -/* - * Transmit and device reset timeout, default 5 seconds. - */ -static int watchdog = 5000; - -/* Hardware structures */ - -/** - * struct rx_ring_desc - Receive descriptor ring element - * @status: The status of the descriptor element (E.g. who owns it) - * @length: The number of bytes in the block pointed to by data_ptr - * @data_ptr: The physical address of the data block to receive into - * @next_desc: The physical address of the next descriptor element. - */ -struct rx_ring_desc { - __le32 status; - __le32 length; - __le32 data_ptr; - __le32 next_desc; -}; - -/** - * struct tx_ring_desc - Transmit descriptor ring element - * @owner: Who owns the descriptor - * @status: The number of bytes in the block pointed to by data_ptr - * @data_ptr: The physical address of the data block to receive into - * @next_desc: The physical address of the next descriptor element. - */ -struct tx_ring_desc { - __le32 owner; - __le32 status; - __le32 data_ptr; - __le32 next_desc; -}; - -/** - * struct ks8695_skbuff - sk_buff wrapper for rx/tx rings. - * @skb: The buffer in the ring - * @dma_ptr: The mapped DMA pointer of the buffer - * @length: The number of bytes mapped to dma_ptr - */ -struct ks8695_skbuff { - struct sk_buff *skb; - dma_addr_t dma_ptr; - u32 length; -}; - -/* Private device structure */ - -#define MAX_TX_DESC 8 -#define MAX_TX_DESC_MASK 0x7 -#define MAX_RX_DESC 16 -#define MAX_RX_DESC_MASK 0xf - -/*napi_weight have better more than rx DMA buffers*/ -#define NAPI_WEIGHT 64 - -#define MAX_RXBUF_SIZE 0x700 - -#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC) -#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC) -#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE) - -/** - * enum ks8695_dtype - Device type - * @KS8695_DTYPE_WAN: This device is a WAN interface - * @KS8695_DTYPE_LAN: This device is a LAN interface - * @KS8695_DTYPE_HPNA: This device is an HPNA interface - */ -enum ks8695_dtype { - KS8695_DTYPE_WAN, - KS8695_DTYPE_LAN, - KS8695_DTYPE_HPNA, -}; - -/** - * struct ks8695_priv - Private data for the KS8695 Ethernet - * @in_suspend: Flag to indicate if we're suspending/resuming - * @ndev: The net_device for this interface - * @dev: The platform device object for this interface - * @dtype: The type of this device - * @io_regs: The ioremapped registers for this interface - * @napi : Add support NAPI for Rx - * @rx_irq_name: The textual name of the RX IRQ from the platform data - * @tx_irq_name: The textual name of the TX IRQ from the platform data - * @link_irq_name: The textual name of the link IRQ from the - * platform data if available - * @rx_irq: The IRQ number for the RX IRQ - * @tx_irq: The IRQ number for the TX IRQ - * @link_irq: The IRQ number for the link IRQ if available - * @regs_req: The resource request for the registers region - * @phyiface_req: The resource request for the phy/switch region - * if available - * @phyiface_regs: The ioremapped registers for the phy/switch if available - * @ring_base: The base pointer of the dma coherent memory for the rings - * @ring_base_dma: The DMA mapped equivalent of ring_base - * @tx_ring: The pointer in ring_base of the TX ring - * @tx_ring_used: The number of slots in the TX ring which are occupied - * @tx_ring_next_slot: The next slot to fill in the TX ring - * @tx_ring_dma: The DMA mapped equivalent of tx_ring - * @tx_buffers: The sk_buff mappings for the TX ring - * @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables - * @rx_ring: The pointer in ring_base of the RX ring - * @rx_ring_dma: The DMA mapped equivalent of rx_ring - * @rx_buffers: The sk_buff mappings for the RX ring - * @next_rx_desc_read: The next RX descriptor to read from on IRQ - * @rx_lock: A lock to protect Rx irq function - * @msg_enable: The flags for which messages to emit - */ -struct ks8695_priv { - int in_suspend; - struct net_device *ndev; - struct device *dev; - enum ks8695_dtype dtype; - void __iomem *io_regs; - - struct napi_struct napi; - - const char *rx_irq_name, *tx_irq_name, *link_irq_name; - int rx_irq, tx_irq, link_irq; - - struct resource *regs_req, *phyiface_req; - void __iomem *phyiface_regs; - - void *ring_base; - dma_addr_t ring_base_dma; - - struct tx_ring_desc *tx_ring; - int tx_ring_used; - int tx_ring_next_slot; - dma_addr_t tx_ring_dma; - struct ks8695_skbuff tx_buffers[MAX_TX_DESC]; - spinlock_t txq_lock; - - struct rx_ring_desc *rx_ring; - dma_addr_t rx_ring_dma; - struct ks8695_skbuff rx_buffers[MAX_RX_DESC]; - int next_rx_desc_read; - spinlock_t rx_lock; - - int msg_enable; -}; - -/* Register access */ - -/** - * ks8695_readreg - Read from a KS8695 ethernet register - * @ksp: The device to read from - * @reg: The register to read - */ -static inline u32 -ks8695_readreg(struct ks8695_priv *ksp, int reg) -{ - return readl(ksp->io_regs + reg); -} - -/** - * ks8695_writereg - Write to a KS8695 ethernet register - * @ksp: The device to write to - * @reg: The register to write - * @value: The value to write to the register - */ -static inline void -ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value) -{ - writel(value, ksp->io_regs + reg); -} - -/* Utility functions */ - -/** - * ks8695_port_type - Retrieve port-type as user-friendly string - * @ksp: The device to return the type for - * - * Returns a string indicating which of the WAN, LAN or HPNA - * ports this device is likely to represent. - */ -static const char * -ks8695_port_type(struct ks8695_priv *ksp) -{ - switch (ksp->dtype) { - case KS8695_DTYPE_LAN: - return "LAN"; - case KS8695_DTYPE_WAN: - return "WAN"; - case KS8695_DTYPE_HPNA: - return "HPNA"; - } - - return "UNKNOWN"; -} - -/** - * ks8695_update_mac - Update the MAC registers in the device - * @ksp: The device to update - * - * Updates the MAC registers in the KS8695 device from the address in the - * net_device structure associated with this interface. - */ -static void -ks8695_update_mac(struct ks8695_priv *ksp) -{ - /* Update the HW with the MAC from the net_device */ - struct net_device *ndev = ksp->ndev; - u32 machigh, maclow; - - maclow = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) | - (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5] << 0)); - machigh = ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1] << 0)); - - ks8695_writereg(ksp, KS8695_MAL, maclow); - ks8695_writereg(ksp, KS8695_MAH, machigh); - -} - -/** - * ks8695_refill_rxbuffers - Re-fill the RX buffer ring - * @ksp: The device to refill - * - * Iterates the RX ring of the device looking for empty slots. - * For each empty slot, we allocate and map a new SKB and give it - * to the hardware. - * This can be called from interrupt context safely. - */ -static void -ks8695_refill_rxbuffers(struct ks8695_priv *ksp) -{ - /* Run around the RX ring, filling in any missing sk_buff's */ - int buff_n; - - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - if (!ksp->rx_buffers[buff_n].skb) { - struct sk_buff *skb = - netdev_alloc_skb(ksp->ndev, MAX_RXBUF_SIZE); - dma_addr_t mapping; - - ksp->rx_buffers[buff_n].skb = skb; - if (skb == NULL) { - /* Failed to allocate one, perhaps - * we'll try again later. - */ - break; - } - - mapping = dma_map_single(ksp->dev, skb->data, - MAX_RXBUF_SIZE, - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(ksp->dev, mapping))) { - /* Failed to DMA map this SKB, try later */ - dev_kfree_skb_irq(skb); - ksp->rx_buffers[buff_n].skb = NULL; - break; - } - ksp->rx_buffers[buff_n].dma_ptr = mapping; - ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE; - - /* Record this into the DMA ring */ - ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping); - ksp->rx_ring[buff_n].length = - cpu_to_le32(MAX_RXBUF_SIZE); - - wmb(); - - /* And give ownership over to the hardware */ - ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN); - } - } -} - -/* Maximum number of multicast addresses which the KS8695 HW supports */ -#define KS8695_NR_ADDRESSES 16 - -/** - * ks8695_init_partial_multicast - Init the mcast addr registers - * @ksp: The device to initialise - * @addr: The multicast address list to use - * @nr_addr: The number of addresses in the list - * - * This routine is a helper for ks8695_set_multicast - it writes - * the additional-address registers in the KS8695 ethernet device - * and cleans up any others left behind. - */ -static void -ks8695_init_partial_multicast(struct ks8695_priv *ksp, - struct net_device *ndev) -{ - u32 low, high; - int i; - struct netdev_hw_addr *ha; - - i = 0; - netdev_for_each_mc_addr(ha, ndev) { - /* Ran out of space in chip? */ - BUG_ON(i == KS8695_NR_ADDRESSES); - - low = (ha->addr[2] << 24) | (ha->addr[3] << 16) | - (ha->addr[4] << 8) | (ha->addr[5]); - high = (ha->addr[0] << 8) | (ha->addr[1]); - - ks8695_writereg(ksp, KS8695_AAL_(i), low); - ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high); - i++; - } - - /* Clear the remaining Additional Station Addresses */ - for (; i < KS8695_NR_ADDRESSES; i++) { - ks8695_writereg(ksp, KS8695_AAL_(i), 0); - ks8695_writereg(ksp, KS8695_AAH_(i), 0); - } -} - -/* Interrupt handling */ - -/** - * ks8695_tx_irq - Transmit IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * Process the TX ring, clearing out any transmitted slots. - * Allows the net_device to pass us new packets once slots are - * freed. - */ -static irqreturn_t -ks8695_tx_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - int buff_n; - - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - if (ksp->tx_buffers[buff_n].skb && - !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) { - rmb(); - /* An SKB which is not owned by HW is present */ - /* Update the stats for the net_device */ - ndev->stats.tx_packets++; - ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length; - - /* Free the packet from the ring */ - ksp->tx_ring[buff_n].data_ptr = 0; - - /* Free the sk_buff */ - dma_unmap_single(ksp->dev, - ksp->tx_buffers[buff_n].dma_ptr, - ksp->tx_buffers[buff_n].length, - DMA_TO_DEVICE); - dev_consume_skb_irq(ksp->tx_buffers[buff_n].skb); - ksp->tx_buffers[buff_n].skb = NULL; - ksp->tx_ring_used--; - } - } - - netif_wake_queue(ndev); - - return IRQ_HANDLED; -} - -/** - * ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit - * @ksp: Private data for the KS8695 Ethernet - * - * For KS8695 document: - * Interrupt Enable Register (offset 0xE204) - * Bit29 : WAN MAC Receive Interrupt Enable - * Bit16 : LAN MAC Receive Interrupt Enable - * Interrupt Status Register (Offset 0xF208) - * Bit29: WAN MAC Receive Status - * Bit16: LAN MAC Receive Status - * So, this Rx interrupt enable/status bit number is equal - * as Rx IRQ number. - */ -static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp) -{ - return ksp->rx_irq; -} - -/** - * ks8695_rx_irq - Receive IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * Inform NAPI that packet reception needs to be scheduled - */ - -static irqreturn_t -ks8695_rx_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - - spin_lock(&ksp->rx_lock); - - if (napi_schedule_prep(&ksp->napi)) { - unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN); - unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); - /*disable rx interrupt*/ - status &= ~mask_bit; - writel(status , KS8695_IRQ_VA + KS8695_INTEN); - __napi_schedule(&ksp->napi); - } - - spin_unlock(&ksp->rx_lock); - return IRQ_HANDLED; -} - -/** - * ks8695_rx - Receive packets called by NAPI poll method - * @ksp: Private data for the KS8695 Ethernet - * @budget: Number of packets allowed to process - */ -static int ks8695_rx(struct ks8695_priv *ksp, int budget) -{ - struct net_device *ndev = ksp->ndev; - struct sk_buff *skb; - int buff_n; - u32 flags; - int pktlen; - int received = 0; - - buff_n = ksp->next_rx_desc_read; - while (received < budget - && ksp->rx_buffers[buff_n].skb - && (!(ksp->rx_ring[buff_n].status & - cpu_to_le32(RDES_OWN)))) { - rmb(); - flags = le32_to_cpu(ksp->rx_ring[buff_n].status); - - /* Found an SKB which we own, this means we - * received a packet - */ - if ((flags & (RDES_FS | RDES_LS)) != - (RDES_FS | RDES_LS)) { - /* This packet is not the first and - * the last segment. Therefore it is - * a "spanning" packet and we can't - * handle it - */ - goto rx_failure; - } - - if (flags & (RDES_ES | RDES_RE)) { - /* It's an error packet */ - ndev->stats.rx_errors++; - if (flags & RDES_TL) - ndev->stats.rx_length_errors++; - if (flags & RDES_RF) - ndev->stats.rx_length_errors++; - if (flags & RDES_CE) - ndev->stats.rx_crc_errors++; - if (flags & RDES_RE) - ndev->stats.rx_missed_errors++; - - goto rx_failure; - } - - pktlen = flags & RDES_FLEN; - pktlen -= 4; /* Drop the CRC */ - - /* Retrieve the sk_buff */ - skb = ksp->rx_buffers[buff_n].skb; - - /* Clear it from the ring */ - ksp->rx_buffers[buff_n].skb = NULL; - ksp->rx_ring[buff_n].data_ptr = 0; - - /* Unmap the SKB */ - dma_unmap_single(ksp->dev, - ksp->rx_buffers[buff_n].dma_ptr, - ksp->rx_buffers[buff_n].length, - DMA_FROM_DEVICE); - - /* Relinquish the SKB to the network layer */ - skb_put(skb, pktlen); - skb->protocol = eth_type_trans(skb, ndev); - napi_gro_receive(&ksp->napi, skb); - - /* Record stats */ - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += pktlen; - goto rx_finished; - -rx_failure: - /* This ring entry is an error, but we can - * re-use the skb - */ - /* Give the ring entry back to the hardware */ - ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN); -rx_finished: - received++; - buff_n = (buff_n + 1) & MAX_RX_DESC_MASK; - } - - /* And note which RX descriptor we last did */ - ksp->next_rx_desc_read = buff_n; - - /* And refill the buffers */ - ks8695_refill_rxbuffers(ksp); - - /* Kick the RX DMA engine, in case it became suspended */ - ks8695_writereg(ksp, KS8695_DRSC, 0); - - return received; -} - - -/** - * ks8695_poll - Receive packet by NAPI poll method - * @ksp: Private data for the KS8695 Ethernet - * @budget: The remaining number packets for network subsystem - * - * Invoked by the network core when it requests for new - * packets from the driver - */ -static int ks8695_poll(struct napi_struct *napi, int budget) -{ - struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi); - unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN); - unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp); - int work_done; - - work_done = ks8695_rx(ksp, budget); - - if (work_done < budget && napi_complete_done(napi, work_done)) { - unsigned long flags; - - spin_lock_irqsave(&ksp->rx_lock, flags); - /* enable rx interrupt */ - writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN); - spin_unlock_irqrestore(&ksp->rx_lock, flags); - } - return work_done; -} - -/** - * ks8695_link_irq - Link change IRQ handler - * @irq: The IRQ which went off (ignored) - * @dev_id: The net_device for the interrupt - * - * The WAN interface can generate an IRQ when the link changes, - * report this to the net layer and the user. - */ -static irqreturn_t -ks8695_link_irq(int irq, void *dev_id) -{ - struct net_device *ndev = (struct net_device *)dev_id; - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - if (ctrl & WMC_WLS) { - netif_carrier_on(ndev); - if (netif_msg_link(ksp)) - dev_info(ksp->dev, - "%s: Link is now up (10%sMbps/%s-duplex)\n", - ndev->name, - (ctrl & WMC_WSS) ? "0" : "", - (ctrl & WMC_WDS) ? "Full" : "Half"); - } else { - netif_carrier_off(ndev); - if (netif_msg_link(ksp)) - dev_info(ksp->dev, "%s: Link is now down.\n", - ndev->name); - } - - return IRQ_HANDLED; -} - - -/* KS8695 Device functions */ - -/** - * ks8695_reset - Reset a KS8695 ethernet interface - * @ksp: The interface to reset - * - * Perform an engine reset of the interface and re-program it - * with sensible defaults. - */ -static void -ks8695_reset(struct ks8695_priv *ksp) -{ - int reset_timeout = watchdog; - /* Issue the reset via the TX DMA control register */ - ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST); - while (reset_timeout--) { - if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST)) - break; - msleep(1); - } - - if (reset_timeout < 0) { - dev_crit(ksp->dev, - "Timeout waiting for DMA engines to reset\n"); - /* And blithely carry on */ - } - - /* Definitely wait long enough before attempting to program - * the engines - */ - msleep(10); - - /* RX: unicast and broadcast */ - ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB); - /* TX: pad and add CRC */ - ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC); -} - -/** - * ks8695_shutdown - Shut down a KS8695 ethernet interface - * @ksp: The interface to shut down - * - * This disables packet RX/TX, cleans up IRQs, drains the rings, - * and basically places the interface into a clean shutdown - * state. - */ -static void -ks8695_shutdown(struct ks8695_priv *ksp) -{ - u32 ctrl; - int buff_n; - - /* Disable packet transmission */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE); - - /* Disable packet reception */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE); - - /* Release the IRQs */ - free_irq(ksp->rx_irq, ksp->ndev); - free_irq(ksp->tx_irq, ksp->ndev); - if (ksp->link_irq != -1) - free_irq(ksp->link_irq, ksp->ndev); - - /* Throw away any pending TX packets */ - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - if (ksp->tx_buffers[buff_n].skb) { - /* Remove this SKB from the TX ring */ - ksp->tx_ring[buff_n].owner = 0; - ksp->tx_ring[buff_n].status = 0; - ksp->tx_ring[buff_n].data_ptr = 0; - - /* Unmap and bin this SKB */ - dma_unmap_single(ksp->dev, - ksp->tx_buffers[buff_n].dma_ptr, - ksp->tx_buffers[buff_n].length, - DMA_TO_DEVICE); - dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb); - ksp->tx_buffers[buff_n].skb = NULL; - } - } - - /* Purge the RX buffers */ - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - if (ksp->rx_buffers[buff_n].skb) { - /* Remove the SKB from the RX ring */ - ksp->rx_ring[buff_n].status = 0; - ksp->rx_ring[buff_n].data_ptr = 0; - - /* Unmap and bin the SKB */ - dma_unmap_single(ksp->dev, - ksp->rx_buffers[buff_n].dma_ptr, - ksp->rx_buffers[buff_n].length, - DMA_FROM_DEVICE); - dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb); - ksp->rx_buffers[buff_n].skb = NULL; - } - } -} - - -/** - * ks8695_setup_irq - IRQ setup helper function - * @irq: The IRQ number to claim - * @irq_name: The name to give the IRQ claimant - * @handler: The function to call to handle the IRQ - * @ndev: The net_device to pass in as the dev_id argument to the handler - * - * Return 0 on success. - */ -static int -ks8695_setup_irq(int irq, const char *irq_name, - irq_handler_t handler, struct net_device *ndev) -{ - int ret; - - ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev); - - if (ret) { - dev_err(&ndev->dev, "failure to request IRQ %d\n", irq); - return ret; - } - - return 0; -} - -/** - * ks8695_init_net - Initialise a KS8695 ethernet interface - * @ksp: The interface to initialise - * - * This routine fills the RX ring, initialises the DMA engines, - * allocates the IRQs and then starts the packet TX and RX - * engines. - */ -static int -ks8695_init_net(struct ks8695_priv *ksp) -{ - int ret; - u32 ctrl; - - ks8695_refill_rxbuffers(ksp); - - /* Initialise the DMA engines */ - ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma); - ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma); - - /* Request the IRQs */ - ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name, - ks8695_rx_irq, ksp->ndev); - if (ret) - return ret; - ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name, - ks8695_tx_irq, ksp->ndev); - if (ret) - return ret; - if (ksp->link_irq != -1) { - ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name, - ks8695_link_irq, ksp->ndev); - if (ret) - return ret; - } - - /* Set up the ring indices */ - ksp->next_rx_desc_read = 0; - ksp->tx_ring_next_slot = 0; - ksp->tx_ring_used = 0; - - /* Bring up transmission */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - /* Enable packet transmission */ - ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE); - - /* Bring up the reception */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - /* Enable packet reception */ - ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE); - /* And start the DMA engine */ - ks8695_writereg(ksp, KS8695_DRSC, 0); - - /* All done */ - return 0; -} - -/** - * ks8695_release_device - HW resource release for KS8695 e-net - * @ksp: The device to be freed - * - * This unallocates io memory regions, dma-coherent regions etc - * which were allocated in ks8695_probe. - */ -static void -ks8695_release_device(struct ks8695_priv *ksp) -{ - /* Unmap the registers */ - iounmap(ksp->io_regs); - if (ksp->phyiface_regs) - iounmap(ksp->phyiface_regs); - - /* And release the request */ - release_resource(ksp->regs_req); - kfree(ksp->regs_req); - if (ksp->phyiface_req) { - release_resource(ksp->phyiface_req); - kfree(ksp->phyiface_req); - } - - /* Free the ring buffers */ - dma_free_coherent(ksp->dev, RING_DMA_SIZE, - ksp->ring_base, ksp->ring_base_dma); -} - -/* Ethtool support */ - -/** - * ks8695_get_msglevel - Get the messages enabled for emission - * @ndev: The network device to read from - */ -static u32 -ks8695_get_msglevel(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - return ksp->msg_enable; -} - -/** - * ks8695_set_msglevel - Set the messages enabled for emission - * @ndev: The network device to configure - * @value: The messages to set for emission - */ -static void -ks8695_set_msglevel(struct net_device *ndev, u32 value) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - ksp->msg_enable = value; -} - -/** - * ks8695_wan_get_link_ksettings - Get device-specific settings. - * @ndev: The network device to read settings from - * @cmd: The ethtool structure to read into - */ -static int -ks8695_wan_get_link_ksettings(struct net_device *ndev, - struct ethtool_link_ksettings *cmd) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - u32 supported, advertising; - - /* All ports on the KS8695 support these... */ - supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | - SUPPORTED_TP | SUPPORTED_MII); - - advertising = ADVERTISED_TP | ADVERTISED_MII; - cmd->base.port = PORT_MII; - supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause); - cmd->base.phy_address = 0; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - if ((ctrl & WMC_WAND) == 0) { - /* auto-negotiation is enabled */ - advertising |= ADVERTISED_Autoneg; - if (ctrl & WMC_WANA100F) - advertising |= ADVERTISED_100baseT_Full; - if (ctrl & WMC_WANA100H) - advertising |= ADVERTISED_100baseT_Half; - if (ctrl & WMC_WANA10F) - advertising |= ADVERTISED_10baseT_Full; - if (ctrl & WMC_WANA10H) - advertising |= ADVERTISED_10baseT_Half; - if (ctrl & WMC_WANAP) - advertising |= ADVERTISED_Pause; - cmd->base.autoneg = AUTONEG_ENABLE; - - cmd->base.speed = (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10; - cmd->base.duplex = (ctrl & WMC_WDS) ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - /* auto-negotiation is disabled */ - cmd->base.autoneg = AUTONEG_DISABLE; - - cmd->base.speed = (ctrl & WMC_WANF100) ? - SPEED_100 : SPEED_10; - cmd->base.duplex = (ctrl & WMC_WANFF) ? - DUPLEX_FULL : DUPLEX_HALF; - } - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - advertising); - - return 0; -} - -/** - * ks8695_wan_set_link_ksettings - Set device-specific settings. - * @ndev: The network device to configure - * @cmd: The settings to configure - */ -static int -ks8695_wan_set_link_ksettings(struct net_device *ndev, - const struct ethtool_link_ksettings *cmd) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - u32 advertising; - - ethtool_convert_link_mode_to_legacy_u32(&advertising, - cmd->link_modes.advertising); - - if ((cmd->base.speed != SPEED_10) && (cmd->base.speed != SPEED_100)) - return -EINVAL; - if ((cmd->base.duplex != DUPLEX_HALF) && - (cmd->base.duplex != DUPLEX_FULL)) - return -EINVAL; - if (cmd->base.port != PORT_MII) - return -EINVAL; - if ((cmd->base.autoneg != AUTONEG_DISABLE) && - (cmd->base.autoneg != AUTONEG_ENABLE)) - return -EINVAL; - - if (cmd->base.autoneg == AUTONEG_ENABLE) { - if ((advertising & (ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full)) == 0) - return -EINVAL; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H | - WMC_WANA10F | WMC_WANA10H); - if (advertising & ADVERTISED_100baseT_Full) - ctrl |= WMC_WANA100F; - if (advertising & ADVERTISED_100baseT_Half) - ctrl |= WMC_WANA100H; - if (advertising & ADVERTISED_10baseT_Full) - ctrl |= WMC_WANA10F; - if (advertising & ADVERTISED_10baseT_Half) - ctrl |= WMC_WANA10H; - - /* force a re-negotiation */ - ctrl |= WMC_WANR; - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - } else { - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - /* disable auto-negotiation */ - ctrl |= WMC_WAND; - ctrl &= ~(WMC_WANF100 | WMC_WANFF); - - if (cmd->base.speed == SPEED_100) - ctrl |= WMC_WANF100; - if (cmd->base.duplex == DUPLEX_FULL) - ctrl |= WMC_WANFF; - - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - } - - return 0; -} - -/** - * ks8695_wan_nwayreset - Restart the autonegotiation on the port. - * @ndev: The network device to restart autoneotiation on - */ -static int -ks8695_wan_nwayreset(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - if ((ctrl & WMC_WAND) == 0) - writel(ctrl | WMC_WANR, - ksp->phyiface_regs + KS8695_WMC); - else - /* auto-negotiation not enabled */ - return -EINVAL; - - return 0; -} - -/** - * ks8695_wan_get_pause - Retrieve network pause/flow-control advertising - * @ndev: The device to retrieve settings from - * @param: The structure to fill out with the information - */ -static void -ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = readl(ksp->phyiface_regs + KS8695_WMC); - - /* advertise Pause */ - param->autoneg = (ctrl & WMC_WANAP); - - /* current Rx Flow-control */ - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - param->rx_pause = (ctrl & DRXC_RFCE); - - /* current Tx Flow-control */ - ctrl = ks8695_readreg(ksp, KS8695_DTXC); - param->tx_pause = (ctrl & DTXC_TFCE); -} - -/** - * ks8695_get_drvinfo - Retrieve driver information - * @ndev: The network device to retrieve info about - * @info: The info structure to fill out. - */ -static void -ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, MODULENAME, sizeof(info->driver)); - strlcpy(info->version, MODULEVERSION, sizeof(info->version)); - strlcpy(info->bus_info, dev_name(ndev->dev.parent), - sizeof(info->bus_info)); -} - -static const struct ethtool_ops ks8695_ethtool_ops = { - .get_msglevel = ks8695_get_msglevel, - .set_msglevel = ks8695_set_msglevel, - .get_drvinfo = ks8695_get_drvinfo, -}; - -static const struct ethtool_ops ks8695_wan_ethtool_ops = { - .get_msglevel = ks8695_get_msglevel, - .set_msglevel = ks8695_set_msglevel, - .nway_reset = ks8695_wan_nwayreset, - .get_link = ethtool_op_get_link, - .get_pauseparam = ks8695_wan_get_pause, - .get_drvinfo = ks8695_get_drvinfo, - .get_link_ksettings = ks8695_wan_get_link_ksettings, - .set_link_ksettings = ks8695_wan_set_link_ksettings, -}; - -/* Network device interface functions */ - -/** - * ks8695_set_mac - Update MAC in net dev and HW - * @ndev: The network device to update - * @addr: The new MAC address to set - */ -static int -ks8695_set_mac(struct net_device *ndev, void *addr) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - struct sockaddr *address = addr; - - if (!is_valid_ether_addr(address->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len); - - ks8695_update_mac(ksp); - - dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n", - ndev->name, ndev->dev_addr); - - return 0; -} - -/** - * ks8695_set_multicast - Set up the multicast behaviour of the interface - * @ndev: The net_device to configure - * - * This routine, called by the net layer, configures promiscuity - * and multicast reception behaviour for the interface. - */ -static void -ks8695_set_multicast(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - u32 ctrl; - - ctrl = ks8695_readreg(ksp, KS8695_DRXC); - - if (ndev->flags & IFF_PROMISC) { - /* enable promiscuous mode */ - ctrl |= DRXC_RA; - } else if (ndev->flags & ~IFF_PROMISC) { - /* disable promiscuous mode */ - ctrl &= ~DRXC_RA; - } - - if (ndev->flags & IFF_ALLMULTI) { - /* enable all multicast mode */ - ctrl |= DRXC_RM; - } else if (netdev_mc_count(ndev) > KS8695_NR_ADDRESSES) { - /* more specific multicast addresses than can be - * handled in hardware - */ - ctrl |= DRXC_RM; - } else { - /* enable specific multicasts */ - ctrl &= ~DRXC_RM; - ks8695_init_partial_multicast(ksp, ndev); - } - - ks8695_writereg(ksp, KS8695_DRXC, ctrl); -} - -/** - * ks8695_timeout - Handle a network tx/rx timeout. - * @ndev: The net_device which timed out. - * - * A network transaction timed out, reset the device. - */ -static void -ks8695_timeout(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_stop_queue(ndev); - ks8695_shutdown(ksp); - - ks8695_reset(ksp); - - ks8695_update_mac(ksp); - - /* We ignore the return from this since it managed to init - * before it probably will be okay to init again. - */ - ks8695_init_net(ksp); - - /* Reconfigure promiscuity etc */ - ks8695_set_multicast(ndev); - - /* And start the TX queue once more */ - netif_start_queue(ndev); -} - -/** - * ks8695_start_xmit - Start a packet transmission - * @skb: The packet to transmit - * @ndev: The network device to send the packet on - * - * This routine, called by the net layer, takes ownership of the - * sk_buff and adds it to the TX ring. It then kicks the TX DMA - * engine to ensure transmission begins. - */ -static netdev_tx_t -ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - int buff_n; - dma_addr_t dmap; - - spin_lock_irq(&ksp->txq_lock); - - if (ksp->tx_ring_used == MAX_TX_DESC) { - /* Somehow we got entered when we have no room */ - spin_unlock_irq(&ksp->txq_lock); - return NETDEV_TX_BUSY; - } - - buff_n = ksp->tx_ring_next_slot; - - BUG_ON(ksp->tx_buffers[buff_n].skb); - - dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(ksp->dev, dmap))) { - /* Failed to DMA map this SKB, give it back for now */ - spin_unlock_irq(&ksp->txq_lock); - dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\ - "transmission, trying later\n", ndev->name); - return NETDEV_TX_BUSY; - } - - ksp->tx_buffers[buff_n].dma_ptr = dmap; - /* Mapped okay, store the buffer pointer and length for later */ - ksp->tx_buffers[buff_n].skb = skb; - ksp->tx_buffers[buff_n].length = skb->len; - - /* Fill out the TX descriptor */ - ksp->tx_ring[buff_n].data_ptr = - cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr); - ksp->tx_ring[buff_n].status = - cpu_to_le32(TDES_IC | TDES_FS | TDES_LS | - (skb->len & TDES_TBS)); - - wmb(); - - /* Hand it over to the hardware */ - ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN); - - if (++ksp->tx_ring_used == MAX_TX_DESC) - netif_stop_queue(ndev); - - /* Kick the TX DMA in case it decided to go IDLE */ - ks8695_writereg(ksp, KS8695_DTSC, 0); - - /* And update the next ring slot */ - ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK; - - spin_unlock_irq(&ksp->txq_lock); - return NETDEV_TX_OK; -} - -/** - * ks8695_stop - Stop (shutdown) a KS8695 ethernet interface - * @ndev: The net_device to stop - * - * This disables the TX queue and cleans up a KS8695 ethernet - * device. - */ -static int -ks8695_stop(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_stop_queue(ndev); - napi_disable(&ksp->napi); - - ks8695_shutdown(ksp); - - return 0; -} - -/** - * ks8695_open - Open (bring up) a KS8695 ethernet interface - * @ndev: The net_device to open - * - * This resets, configures the MAC, initialises the RX ring and - * DMA engines and starts the TX queue for a KS8695 ethernet - * device. - */ -static int -ks8695_open(struct net_device *ndev) -{ - struct ks8695_priv *ksp = netdev_priv(ndev); - int ret; - - ks8695_reset(ksp); - - ks8695_update_mac(ksp); - - ret = ks8695_init_net(ksp); - if (ret) { - ks8695_shutdown(ksp); - return ret; - } - - napi_enable(&ksp->napi); - netif_start_queue(ndev); - - return 0; -} - -/* Platform device driver */ - -/** - * ks8695_init_switch - Init LAN switch to known good defaults. - * @ksp: The device to initialise - * - * This initialises the LAN switch in the KS8695 to a known-good - * set of defaults. - */ -static void -ks8695_init_switch(struct ks8695_priv *ksp) -{ - u32 ctrl; - - /* Default value for SEC0 according to datasheet */ - ctrl = 0x40819e00; - - /* LED0 = Speed LED1 = Link/Activity */ - ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S); - ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY); - - /* Enable Switch */ - ctrl |= SEC0_ENABLE; - - writel(ctrl, ksp->phyiface_regs + KS8695_SEC0); - - /* Defaults for SEC1 */ - writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1); -} - -/** - * ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults - * @ksp: The device to initialise - * - * This initialises a KS8695's WAN phy to sensible values for - * autonegotiation etc. - */ -static void -ks8695_init_wan_phy(struct ks8695_priv *ksp) -{ - u32 ctrl; - - /* Support auto-negotiation */ - ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H | - WMC_WANA10F | WMC_WANA10H); - - /* LED0 = Activity , LED1 = Link */ - ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK); - - /* Restart Auto-negotiation */ - ctrl |= WMC_WANR; - - writel(ctrl, ksp->phyiface_regs + KS8695_WMC); - - writel(0, ksp->phyiface_regs + KS8695_WPPM); - writel(0, ksp->phyiface_regs + KS8695_PPS); -} - -static const struct net_device_ops ks8695_netdev_ops = { - .ndo_open = ks8695_open, - .ndo_stop = ks8695_stop, - .ndo_start_xmit = ks8695_start_xmit, - .ndo_tx_timeout = ks8695_timeout, - .ndo_set_mac_address = ks8695_set_mac, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = ks8695_set_multicast, -}; - -/** - * ks8695_probe - Probe and initialise a KS8695 ethernet interface - * @pdev: The platform device to probe - * - * Initialise a KS8695 ethernet device from platform data. - * - * This driver requires at least one IORESOURCE_MEM for the - * registers and two IORESOURCE_IRQ for the RX and TX IRQs - * respectively. It can optionally take an additional - * IORESOURCE_MEM for the switch or phy in the case of the lan or - * wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan - * port. - */ -static int -ks8695_probe(struct platform_device *pdev) -{ - struct ks8695_priv *ksp; - struct net_device *ndev; - struct resource *regs_res, *phyiface_res; - struct resource *rxirq_res, *txirq_res, *linkirq_res; - int ret = 0; - int buff_n; - bool inv_mac_addr = false; - u32 machigh, maclow; - - /* Initialise a net_device */ - ndev = alloc_etherdev(sizeof(struct ks8695_priv)); - if (!ndev) - return -ENOMEM; - - SET_NETDEV_DEV(ndev, &pdev->dev); - - dev_dbg(&pdev->dev, "ks8695_probe() called\n"); - - /* Configure our private structure a little */ - ksp = netdev_priv(ndev); - - ksp->dev = &pdev->dev; - ksp->ndev = ndev; - ksp->msg_enable = NETIF_MSG_LINK; - - /* Retrieve resources */ - regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - - rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); - - if (!(regs_res && rxirq_res && txirq_res)) { - dev_err(ksp->dev, "insufficient resources\n"); - ret = -ENOENT; - goto failure; - } - - ksp->regs_req = request_mem_region(regs_res->start, - resource_size(regs_res), - pdev->name); - - if (!ksp->regs_req) { - dev_err(ksp->dev, "cannot claim register space\n"); - ret = -EIO; - goto failure; - } - - ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res)); - - if (!ksp->io_regs) { - dev_err(ksp->dev, "failed to ioremap registers\n"); - ret = -EINVAL; - goto failure; - } - - if (phyiface_res) { - ksp->phyiface_req = - request_mem_region(phyiface_res->start, - resource_size(phyiface_res), - phyiface_res->name); - - if (!ksp->phyiface_req) { - dev_err(ksp->dev, - "cannot claim switch register space\n"); - ret = -EIO; - goto failure; - } - - ksp->phyiface_regs = ioremap(phyiface_res->start, - resource_size(phyiface_res)); - - if (!ksp->phyiface_regs) { - dev_err(ksp->dev, - "failed to ioremap switch registers\n"); - ret = -EINVAL; - goto failure; - } - } - - ksp->rx_irq = rxirq_res->start; - ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX"; - ksp->tx_irq = txirq_res->start; - ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX"; - ksp->link_irq = (linkirq_res ? linkirq_res->start : -1); - ksp->link_irq_name = (linkirq_res && linkirq_res->name) ? - linkirq_res->name : "Ethernet Link"; - - /* driver system setup */ - ndev->netdev_ops = &ks8695_netdev_ops; - ndev->watchdog_timeo = msecs_to_jiffies(watchdog); - - netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT); - - /* Retrieve the default MAC addr from the chip. */ - /* The bootloader should have left it in there for us. */ - - machigh = ks8695_readreg(ksp, KS8695_MAH); - maclow = ks8695_readreg(ksp, KS8695_MAL); - - ndev->dev_addr[0] = (machigh >> 8) & 0xFF; - ndev->dev_addr[1] = machigh & 0xFF; - ndev->dev_addr[2] = (maclow >> 24) & 0xFF; - ndev->dev_addr[3] = (maclow >> 16) & 0xFF; - ndev->dev_addr[4] = (maclow >> 8) & 0xFF; - ndev->dev_addr[5] = maclow & 0xFF; - - if (!is_valid_ether_addr(ndev->dev_addr)) - inv_mac_addr = true; - - /* In order to be efficient memory-wise, we allocate both - * rings in one go. - */ - ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE, - &ksp->ring_base_dma, GFP_KERNEL); - if (!ksp->ring_base) { - ret = -ENOMEM; - goto failure; - } - - /* Specify the TX DMA ring buffer */ - ksp->tx_ring = ksp->ring_base; - ksp->tx_ring_dma = ksp->ring_base_dma; - - /* And initialise the queue's lock */ - spin_lock_init(&ksp->txq_lock); - spin_lock_init(&ksp->rx_lock); - - /* Specify the RX DMA ring buffer */ - ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE; - ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE; - - /* Zero the descriptor rings */ - memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE); - memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE); - - /* Build the rings */ - for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) { - ksp->tx_ring[buff_n].next_desc = - cpu_to_le32(ksp->tx_ring_dma + - (sizeof(struct tx_ring_desc) * - ((buff_n + 1) & MAX_TX_DESC_MASK))); - } - - for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) { - ksp->rx_ring[buff_n].next_desc = - cpu_to_le32(ksp->rx_ring_dma + - (sizeof(struct rx_ring_desc) * - ((buff_n + 1) & MAX_RX_DESC_MASK))); - } - - /* Initialise the port (physically) */ - if (ksp->phyiface_regs && ksp->link_irq == -1) { - ks8695_init_switch(ksp); - ksp->dtype = KS8695_DTYPE_LAN; - ndev->ethtool_ops = &ks8695_ethtool_ops; - } else if (ksp->phyiface_regs && ksp->link_irq != -1) { - ks8695_init_wan_phy(ksp); - ksp->dtype = KS8695_DTYPE_WAN; - ndev->ethtool_ops = &ks8695_wan_ethtool_ops; - } else { - /* No initialisation since HPNA does not have a PHY */ - ksp->dtype = KS8695_DTYPE_HPNA; - ndev->ethtool_ops = &ks8695_ethtool_ops; - } - - /* And bring up the net_device with the net core */ - platform_set_drvdata(pdev, ndev); - ret = register_netdev(ndev); - - if (ret == 0) { - if (inv_mac_addr) - dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please set using ip\n", - ndev->name); - dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n", - ks8695_port_type(ksp), ndev->dev_addr); - } else { - /* Report the failure to register the net_device */ - dev_err(ksp->dev, "ks8695net: failed to register netdev.\n"); - goto failure; - } - - /* All is well */ - return 0; - - /* Error exit path */ -failure: - ks8695_release_device(ksp); - free_netdev(ndev); - - return ret; -} - -/** - * ks8695_drv_suspend - Suspend a KS8695 ethernet platform device. - * @pdev: The device to suspend - * @state: The suspend state - * - * This routine detaches and shuts down a KS8695 ethernet device. - */ -static int -ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - ksp->in_suspend = 1; - - if (netif_running(ndev)) { - netif_device_detach(ndev); - ks8695_shutdown(ksp); - } - - return 0; -} - -/** - * ks8695_drv_resume - Resume a KS8695 ethernet platform device. - * @pdev: The device to resume - * - * This routine re-initialises and re-attaches a KS8695 ethernet - * device. - */ -static int -ks8695_drv_resume(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - if (netif_running(ndev)) { - ks8695_reset(ksp); - ks8695_init_net(ksp); - ks8695_set_multicast(ndev); - netif_device_attach(ndev); - } - - ksp->in_suspend = 0; - - return 0; -} - -/** - * ks8695_drv_remove - Remove a KS8695 net device on driver unload. - * @pdev: The platform device to remove - * - * This unregisters and releases a KS8695 ethernet device. - */ -static int -ks8695_drv_remove(struct platform_device *pdev) -{ - struct net_device *ndev = platform_get_drvdata(pdev); - struct ks8695_priv *ksp = netdev_priv(ndev); - - netif_napi_del(&ksp->napi); - - unregister_netdev(ndev); - ks8695_release_device(ksp); - free_netdev(ndev); - - dev_dbg(&pdev->dev, "released and freed device\n"); - return 0; -} - -static struct platform_driver ks8695_driver = { - .driver = { - .name = MODULENAME, - }, - .probe = ks8695_probe, - .remove = ks8695_drv_remove, - .suspend = ks8695_drv_suspend, - .resume = ks8695_drv_resume, -}; - -module_platform_driver(ks8695_driver); - -MODULE_AUTHOR("Simtec Electronics"); -MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" MODULENAME); - -module_param(watchdog, int, 0400); -MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); diff --git a/drivers/net/ethernet/micrel/ks8695net.h b/drivers/net/ethernet/micrel/ks8695net.h deleted file mode 100644 index b18fad4ad5fd..000000000000 --- a/drivers/net/ethernet/micrel/ks8695net.h +++ /dev/null @@ -1,108 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Micrel KS8695 (Centaur) Ethernet. - * - * Copyright 2008 Simtec Electronics - * Daniel Silverstone - * Vincent Sanders - */ - -#ifndef KS8695NET_H -#define KS8695NET_H - -/* Receive descriptor flags */ -#define RDES_OWN (1 << 31) /* Ownership */ -#define RDES_FS (1 << 30) /* First Descriptor */ -#define RDES_LS (1 << 29) /* Last Descriptor */ -#define RDES_IPE (1 << 28) /* IP Checksum error */ -#define RDES_TCPE (1 << 27) /* TCP Checksum error */ -#define RDES_UDPE (1 << 26) /* UDP Checksum error */ -#define RDES_ES (1 << 25) /* Error summary */ -#define RDES_MF (1 << 24) /* Multicast Frame */ -#define RDES_RE (1 << 19) /* MII Error reported */ -#define RDES_TL (1 << 18) /* Frame too Long */ -#define RDES_RF (1 << 17) /* Runt Frame */ -#define RDES_CE (1 << 16) /* CRC error */ -#define RDES_FT (1 << 15) /* Frame Type */ -#define RDES_FLEN (0x7ff) /* Frame Length */ - -#define RDES_RER (1 << 25) /* Receive End of Ring */ -#define RDES_RBS (0x7ff) /* Receive Buffer Size */ - -/* Transmit descriptor flags */ - -#define TDES_OWN (1 << 31) /* Ownership */ - -#define TDES_IC (1 << 31) /* Interrupt on Completion */ -#define TDES_FS (1 << 30) /* First Segment */ -#define TDES_LS (1 << 29) /* Last Segment */ -#define TDES_IPCKG (1 << 28) /* IP Checksum generate */ -#define TDES_TCPCKG (1 << 27) /* TCP Checksum generate */ -#define TDES_UDPCKG (1 << 26) /* UDP Checksum generate */ -#define TDES_TER (1 << 25) /* Transmit End of Ring */ -#define TDES_TBS (0x7ff) /* Transmit Buffer Size */ - -/* - * Network controller register offsets - */ -#define KS8695_DTXC (0x00) /* DMA Transmit Control */ -#define KS8695_DRXC (0x04) /* DMA Receive Control */ -#define KS8695_DTSC (0x08) /* DMA Transmit Start Command */ -#define KS8695_DRSC (0x0c) /* DMA Receive Start Command */ -#define KS8695_TDLB (0x10) /* Transmit Descriptor List - * Base Address - */ -#define KS8695_RDLB (0x14) /* Receive Descriptor List - * Base Address - */ -#define KS8695_MAL (0x18) /* MAC Station Address Low */ -#define KS8695_MAH (0x1c) /* MAC Station Address High */ -#define KS8695_AAL_(n) (0x80 + ((n)*8)) /* MAC Additional - * Station Address - * (0..15) Low - */ -#define KS8695_AAH_(n) (0x84 + ((n)*8)) /* MAC Additional - * Station Address - * (0..15) High - */ - - -/* DMA Transmit Control Register */ -#define DTXC_TRST (1 << 31) /* Soft Reset */ -#define DTXC_TBS (0x3f << 24) /* Transmit Burst Size */ -#define DTXC_TUCG (1 << 18) /* Transmit UDP - * Checksum Generate - */ -#define DTXC_TTCG (1 << 17) /* Transmit TCP - * Checksum Generate - */ -#define DTXC_TICG (1 << 16) /* Transmit IP - * Checksum Generate - */ -#define DTXC_TFCE (1 << 9) /* Transmit Flow - * Control Enable - */ -#define DTXC_TLB (1 << 8) /* Loopback mode */ -#define DTXC_TEP (1 << 2) /* Transmit Enable Padding */ -#define DTXC_TAC (1 << 1) /* Transmit Add CRC */ -#define DTXC_TE (1 << 0) /* TX Enable */ - -/* DMA Receive Control Register */ -#define DRXC_RBS (0x3f << 24) /* Receive Burst Size */ -#define DRXC_RUCC (1 << 18) /* Receive UDP Checksum check */ -#define DRXC_RTCG (1 << 17) /* Receive TCP Checksum check */ -#define DRXC_RICG (1 << 16) /* Receive IP Checksum check */ -#define DRXC_RFCE (1 << 9) /* Receive Flow Control - * Enable - */ -#define DRXC_RB (1 << 6) /* Receive Broadcast */ -#define DRXC_RM (1 << 5) /* Receive Multicast */ -#define DRXC_RU (1 << 4) /* Receive Unicast */ -#define DRXC_RERR (1 << 3) /* Receive Error Frame */ -#define DRXC_RA (1 << 2) /* Receive All */ -#define DRXC_RE (1 << 0) /* RX Enable */ - -/* Additional Station Address High */ -#define AAH_E (1 << 31) /* Address Enabled */ - -#endif /* KS8695NET_H */ From 00d2fbf73d5503bc4b4a412a6a87a7ecb603d481 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:41 +0200 Subject: [PATCH 116/123] net: remove w90p910-ether driver The ARM w90x900 platform is getting removed, so this driver is obsolete. Link: https://lore.kernel.org/r/20190809202749.742267-14-arnd@arndb.de Signed-off-by: Arnd Bergmann --- drivers/net/ethernet/Kconfig | 1 - drivers/net/ethernet/Makefile | 1 - drivers/net/ethernet/nuvoton/Kconfig | 29 - drivers/net/ethernet/nuvoton/Makefile | 6 - drivers/net/ethernet/nuvoton/w90p910_ether.c | 1082 ------------------ 5 files changed, 1119 deletions(-) delete mode 100644 drivers/net/ethernet/nuvoton/Kconfig delete mode 100644 drivers/net/ethernet/nuvoton/Makefile delete mode 100644 drivers/net/ethernet/nuvoton/w90p910_ether.c diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 93a2d4deb27c..dc9dee55976b 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -151,7 +151,6 @@ config NET_NETX To compile this driver as a module, choose M here. The module will be called netx-eth. -source "drivers/net/ethernet/nuvoton/Kconfig" source "drivers/net/ethernet/nvidia/Kconfig" source "drivers/net/ethernet/nxp/Kconfig" source "drivers/net/ethernet/oki-semi/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index fb9155cffcff..4bc3c95562bf 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/ obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/ obj-$(CONFIG_NET_VENDOR_NI) += ni/ obj-$(CONFIG_NET_NETX) += netx-eth.o -obj-$(CONFIG_NET_VENDOR_NUVOTON) += nuvoton/ obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ diff --git a/drivers/net/ethernet/nuvoton/Kconfig b/drivers/net/ethernet/nuvoton/Kconfig deleted file mode 100644 index 325e26c549f8..000000000000 --- a/drivers/net/ethernet/nuvoton/Kconfig +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Nuvoton network device configuration -# - -config NET_VENDOR_NUVOTON - bool "Nuvoton devices" - default y - depends on ARM && ARCH_W90X900 - ---help--- - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about Nuvoton cards. If you say Y, you will be asked - for your specific card in the following questions. - -if NET_VENDOR_NUVOTON - -config W90P910_ETH - tristate "Nuvoton w90p910 Ethernet support" - depends on ARM && ARCH_W90X900 - select PHYLIB - select MII - ---help--- - Say Y here if you want to use built-in Ethernet ports - on w90p910 processor. - -endif # NET_VENDOR_NUVOTON diff --git a/drivers/net/ethernet/nuvoton/Makefile b/drivers/net/ethernet/nuvoton/Makefile deleted file mode 100644 index 66f6e728d54b..000000000000 --- a/drivers/net/ethernet/nuvoton/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Nuvoton network device drivers. -# - -obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c deleted file mode 100644 index 3d73970b3a2e..000000000000 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ /dev/null @@ -1,1082 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2008-2009 Nuvoton technology corporation. - * - * Wan ZongShun - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_MODULE_NAME "w90p910-emc" -#define DRV_MODULE_VERSION "0.1" - -/* Ethernet MAC Registers */ -#define REG_CAMCMR 0x00 -#define REG_CAMEN 0x04 -#define REG_CAMM_BASE 0x08 -#define REG_CAML_BASE 0x0c -#define REG_TXDLSA 0x88 -#define REG_RXDLSA 0x8C -#define REG_MCMDR 0x90 -#define REG_MIID 0x94 -#define REG_MIIDA 0x98 -#define REG_FFTCR 0x9C -#define REG_TSDR 0xa0 -#define REG_RSDR 0xa4 -#define REG_DMARFC 0xa8 -#define REG_MIEN 0xac -#define REG_MISTA 0xb0 -#define REG_CTXDSA 0xcc -#define REG_CTXBSA 0xd0 -#define REG_CRXDSA 0xd4 -#define REG_CRXBSA 0xd8 - -/* mac controller bit */ -#define MCMDR_RXON 0x01 -#define MCMDR_ACP (0x01 << 3) -#define MCMDR_SPCRC (0x01 << 5) -#define MCMDR_TXON (0x01 << 8) -#define MCMDR_FDUP (0x01 << 18) -#define MCMDR_ENMDC (0x01 << 19) -#define MCMDR_OPMOD (0x01 << 20) -#define SWR (0x01 << 24) - -/* cam command regiser */ -#define CAMCMR_AUP 0x01 -#define CAMCMR_AMP (0x01 << 1) -#define CAMCMR_ABP (0x01 << 2) -#define CAMCMR_CCAM (0x01 << 3) -#define CAMCMR_ECMP (0x01 << 4) -#define CAM0EN 0x01 - -/* mac mii controller bit */ -#define MDCCR (0x0a << 20) -#define PHYAD (0x01 << 8) -#define PHYWR (0x01 << 16) -#define PHYBUSY (0x01 << 17) -#define PHYPRESP (0x01 << 18) -#define CAM_ENTRY_SIZE 0x08 - -/* rx and tx status */ -#define TXDS_TXCP (0x01 << 19) -#define RXDS_CRCE (0x01 << 17) -#define RXDS_PTLE (0x01 << 19) -#define RXDS_RXGD (0x01 << 20) -#define RXDS_ALIE (0x01 << 21) -#define RXDS_RP (0x01 << 22) - -/* mac interrupt status*/ -#define MISTA_EXDEF (0x01 << 19) -#define MISTA_TXBERR (0x01 << 24) -#define MISTA_TDU (0x01 << 23) -#define MISTA_RDU (0x01 << 10) -#define MISTA_RXBERR (0x01 << 11) - -#define ENSTART 0x01 -#define ENRXINTR 0x01 -#define ENRXGD (0x01 << 4) -#define ENRXBERR (0x01 << 11) -#define ENTXINTR (0x01 << 16) -#define ENTXCP (0x01 << 18) -#define ENTXABT (0x01 << 21) -#define ENTXBERR (0x01 << 24) -#define ENMDC (0x01 << 19) -#define PHYBUSY (0x01 << 17) -#define MDCCR_VAL 0xa00000 - -/* rx and tx owner bit */ -#define RX_OWEN_DMA (0x01 << 31) -#define RX_OWEN_CPU (~(0x03 << 30)) -#define TX_OWEN_DMA (0x01 << 31) -#define TX_OWEN_CPU (~(0x01 << 31)) - -/* tx frame desc controller bit */ -#define MACTXINTEN 0x04 -#define CRCMODE 0x02 -#define PADDINGMODE 0x01 - -/* fftcr controller bit */ -#define TXTHD (0x03 << 8) -#define BLENGTH (0x01 << 20) - -/* global setting for driver */ -#define RX_DESC_SIZE 50 -#define TX_DESC_SIZE 10 -#define MAX_RBUFF_SZ 0x600 -#define MAX_TBUFF_SZ 0x600 -#define TX_TIMEOUT (HZ/2) -#define DELAY 1000 -#define CAM0 0x0 - -static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg); - -struct w90p910_rxbd { - unsigned int sl; - unsigned int buffer; - unsigned int reserved; - unsigned int next; -}; - -struct w90p910_txbd { - unsigned int mode; - unsigned int buffer; - unsigned int sl; - unsigned int next; -}; - -struct recv_pdesc { - struct w90p910_rxbd desclist[RX_DESC_SIZE]; - char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ]; -}; - -struct tran_pdesc { - struct w90p910_txbd desclist[TX_DESC_SIZE]; - char tran_buf[TX_DESC_SIZE][MAX_TBUFF_SZ]; -}; - -struct w90p910_ether { - struct recv_pdesc *rdesc; - struct tran_pdesc *tdesc; - dma_addr_t rdesc_phys; - dma_addr_t tdesc_phys; - struct platform_device *pdev; - struct resource *res; - struct sk_buff *skb; - struct clk *clk; - struct clk *rmiiclk; - struct mii_if_info mii; - struct timer_list check_timer; - void __iomem *reg; - int rxirq; - int txirq; - unsigned int cur_tx; - unsigned int cur_rx; - unsigned int finish_tx; - unsigned int rx_packets; - unsigned int rx_bytes; - unsigned int start_tx_ptr; - unsigned int start_rx_ptr; - unsigned int linkflag; -}; - -static void update_linkspeed_register(struct net_device *dev, - unsigned int speed, unsigned int duplex) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (speed == SPEED_100) { - /* 100 full/half duplex */ - if (duplex == DUPLEX_FULL) { - val |= (MCMDR_OPMOD | MCMDR_FDUP); - } else { - val |= MCMDR_OPMOD; - val &= ~MCMDR_FDUP; - } - } else { - /* 10 full/half duplex */ - if (duplex == DUPLEX_FULL) { - val |= MCMDR_FDUP; - val &= ~MCMDR_OPMOD; - } else { - val &= ~(MCMDR_FDUP | MCMDR_OPMOD); - } - } - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void update_linkspeed(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int bmsr, bmcr, lpa, speed, duplex; - - pdev = ether->pdev; - - if (!mii_link_ok(ðer->mii)) { - ether->linkflag = 0x0; - netif_carrier_off(dev); - dev_warn(&pdev->dev, "%s: Link down.\n", dev->name); - return; - } - - if (ether->linkflag == 1) - return; - - bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR); - bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR); - - if (bmcr & BMCR_ANENABLE) { - if (!(bmsr & BMSR_ANEGCOMPLETE)) - return; - - lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA); - - if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) - speed = SPEED_100; - else - speed = SPEED_10; - - if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) - duplex = DUPLEX_FULL; - else - duplex = DUPLEX_HALF; - - } else { - speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; - duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; - } - - update_linkspeed_register(dev, speed, duplex); - - dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed, - (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); - ether->linkflag = 0x01; - - netif_carrier_on(dev); -} - -static void w90p910_check_link(struct timer_list *t) -{ - struct w90p910_ether *ether = from_timer(ether, t, check_timer); - struct net_device *dev = ether->mii.dev; - - update_linkspeed(dev); - mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); -} - -static void w90p910_write_cam(struct net_device *dev, - unsigned int x, unsigned char *pval) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int msw, lsw; - - msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3]; - - lsw = (pval[4] << 24) | (pval[5] << 16); - - __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE); - __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE); -} - -static int w90p910_init_desc(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *tdesc; - struct w90p910_rxbd *rdesc; - struct platform_device *pdev; - unsigned int i; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - ether->tdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ðer->tdesc_phys, GFP_KERNEL); - if (!ether->tdesc) - return -ENOMEM; - - ether->rdesc = dma_alloc_coherent(&pdev->dev, sizeof(struct recv_pdesc), - ðer->rdesc_phys, GFP_KERNEL); - if (!ether->rdesc) { - dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ether->tdesc, ether->tdesc_phys); - return -ENOMEM; - } - - for (i = 0; i < TX_DESC_SIZE; i++) { - unsigned int offset; - - tdesc = &(ether->tdesc->desclist[i]); - - if (i == TX_DESC_SIZE - 1) - offset = offsetof(struct tran_pdesc, desclist[0]); - else - offset = offsetof(struct tran_pdesc, desclist[i + 1]); - - tdesc->next = ether->tdesc_phys + offset; - tdesc->buffer = ether->tdesc_phys + - offsetof(struct tran_pdesc, tran_buf[i]); - tdesc->sl = 0; - tdesc->mode = 0; - } - - ether->start_tx_ptr = ether->tdesc_phys; - - for (i = 0; i < RX_DESC_SIZE; i++) { - unsigned int offset; - - rdesc = &(ether->rdesc->desclist[i]); - - if (i == RX_DESC_SIZE - 1) - offset = offsetof(struct recv_pdesc, desclist[0]); - else - offset = offsetof(struct recv_pdesc, desclist[i + 1]); - - rdesc->next = ether->rdesc_phys + offset; - rdesc->sl = RX_OWEN_DMA; - rdesc->buffer = ether->rdesc_phys + - offsetof(struct recv_pdesc, recv_buf[i]); - } - - ether->start_rx_ptr = ether->rdesc_phys; - - return 0; -} - -static void w90p910_set_fifo_threshold(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = TXTHD | BLENGTH; - __raw_writel(val, ether->reg + REG_FFTCR); -} - -static void w90p910_return_default_idle(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - val |= SWR; - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_trigger_rx(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ENSTART, ether->reg + REG_RSDR); -} - -static void w90p910_trigger_tx(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ENSTART, ether->reg + REG_TSDR); -} - -static void w90p910_enable_mac_interrupt(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP; - val |= ENTXBERR | ENRXBERR | ENTXABT; - - __raw_writel(val, ether->reg + REG_MIEN); -} - -static void w90p910_get_and_clear_int(struct net_device *dev, - unsigned int *val) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - *val = __raw_readl(ether->reg + REG_MISTA); - __raw_writel(*val, ether->reg + REG_MISTA); -} - -static void w90p910_set_global_maccmd(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC; - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_enable_cam(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - w90p910_write_cam(dev, CAM0, dev->dev_addr); - - val = __raw_readl(ether->reg + REG_CAMEN); - val |= CAM0EN; - __raw_writel(val, ether->reg + REG_CAMEN); -} - -static void w90p910_enable_cam_command(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP; - __raw_writel(val, ether->reg + REG_CAMCMR); -} - -static void w90p910_enable_tx(struct net_device *dev, unsigned int enable) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (enable) - val |= MCMDR_TXON; - else - val &= ~MCMDR_TXON; - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_enable_rx(struct net_device *dev, unsigned int enable) -{ - struct w90p910_ether *ether = netdev_priv(dev); - unsigned int val; - - val = __raw_readl(ether->reg + REG_MCMDR); - - if (enable) - val |= MCMDR_RXON; - else - val &= ~MCMDR_RXON; - - __raw_writel(val, ether->reg + REG_MCMDR); -} - -static void w90p910_set_curdest(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA); - __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA); -} - -static void w90p910_reset_mac(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - w90p910_enable_tx(dev, 0); - w90p910_enable_rx(dev, 0); - w90p910_set_fifo_threshold(dev); - w90p910_return_default_idle(dev); - - if (!netif_queue_stopped(dev)) - netif_stop_queue(dev); - - w90p910_init_desc(dev); - - netif_trans_update(dev); /* prevent tx timeout */ - ether->cur_tx = 0x0; - ether->finish_tx = 0x0; - ether->cur_rx = 0x0; - - w90p910_set_curdest(dev); - w90p910_enable_cam(dev); - w90p910_enable_cam_command(dev); - w90p910_enable_mac_interrupt(dev); - w90p910_enable_tx(dev, 1); - w90p910_enable_rx(dev, 1); - w90p910_trigger_tx(dev); - w90p910_trigger_rx(dev); - - netif_trans_update(dev); /* prevent tx timeout */ - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -} - -static void w90p910_mdio_write(struct net_device *dev, - int phy_id, int reg, int data) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int val, i; - - pdev = ether->pdev; - - __raw_writel(data, ether->reg + REG_MIID); - - val = (phy_id << 0x08) | reg; - val |= PHYBUSY | PHYWR | MDCCR_VAL; - __raw_writel(val, ether->reg + REG_MIIDA); - - for (i = 0; i < DELAY; i++) { - if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) - break; - } - - if (i == DELAY) - dev_warn(&pdev->dev, "mdio write timed out\n"); -} - -static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - unsigned int val, i, data; - - pdev = ether->pdev; - - val = (phy_id << 0x08) | reg; - val |= PHYBUSY | MDCCR_VAL; - __raw_writel(val, ether->reg + REG_MIIDA); - - for (i = 0; i < DELAY; i++) { - if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0) - break; - } - - if (i == DELAY) { - dev_warn(&pdev->dev, "mdio read timed out\n"); - data = 0xffff; - } else { - data = __raw_readl(ether->reg + REG_MIID); - } - - return data; -} - -static int w90p910_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *address = addr; - - if (!is_valid_ether_addr(address->sa_data)) - return -EADDRNOTAVAIL; - - memcpy(dev->dev_addr, address->sa_data, dev->addr_len); - w90p910_write_cam(dev, CAM0, dev->dev_addr); - - return 0; -} - -static int w90p910_ether_close(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - - pdev = ether->pdev; - - dma_free_coherent(&pdev->dev, sizeof(struct recv_pdesc), - ether->rdesc, ether->rdesc_phys); - dma_free_coherent(&pdev->dev, sizeof(struct tran_pdesc), - ether->tdesc, ether->tdesc_phys); - - netif_stop_queue(dev); - - del_timer_sync(ðer->check_timer); - clk_disable(ether->rmiiclk); - clk_disable(ether->clk); - - free_irq(ether->txirq, dev); - free_irq(ether->rxirq, dev); - - return 0; -} - -static int w90p910_send_frame(struct net_device *dev, - unsigned char *data, int length) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *txbd; - struct platform_device *pdev; - unsigned char *buffer; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - txbd = ðer->tdesc->desclist[ether->cur_tx]; - buffer = ether->tdesc->tran_buf[ether->cur_tx]; - - if (length > 1514) { - dev_err(&pdev->dev, "send data %d bytes, check it\n", length); - length = 1514; - } - - txbd->sl = length & 0xFFFF; - - memcpy(buffer, data, length); - - txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN; - - w90p910_enable_tx(dev, 1); - - w90p910_trigger_tx(dev); - - if (++ether->cur_tx >= TX_DESC_SIZE) - ether->cur_tx = 0; - - txbd = ðer->tdesc->desclist[ether->cur_tx]; - - if (txbd->mode & TX_OWEN_DMA) - netif_stop_queue(dev); - - return 0; -} - -static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - if (!(w90p910_send_frame(dev, skb->data, skb->len))) { - ether->skb = skb; - dev_consume_skb_irq(skb); - return 0; - } - return -EAGAIN; -} - -static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) -{ - struct w90p910_ether *ether; - struct w90p910_txbd *txbd; - struct platform_device *pdev; - struct net_device *dev; - unsigned int cur_entry, entry, status; - - dev = dev_id; - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_get_and_clear_int(dev, &status); - - cur_entry = __raw_readl(ether->reg + REG_CTXDSA); - - entry = ether->tdesc_phys + - offsetof(struct tran_pdesc, desclist[ether->finish_tx]); - - while (entry != cur_entry) { - txbd = ðer->tdesc->desclist[ether->finish_tx]; - - if (++ether->finish_tx >= TX_DESC_SIZE) - ether->finish_tx = 0; - - if (txbd->sl & TXDS_TXCP) { - dev->stats.tx_packets++; - dev->stats.tx_bytes += txbd->sl & 0xFFFF; - } else { - dev->stats.tx_errors++; - } - - txbd->sl = 0x0; - txbd->mode = 0x0; - - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - - entry = ether->tdesc_phys + - offsetof(struct tran_pdesc, desclist[ether->finish_tx]); - } - - if (status & MISTA_EXDEF) { - dev_err(&pdev->dev, "emc defer exceed interrupt\n"); - } else if (status & MISTA_TXBERR) { - dev_err(&pdev->dev, "emc bus error interrupt\n"); - w90p910_reset_mac(dev); - } else if (status & MISTA_TDU) { - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); - } - - return IRQ_HANDLED; -} - -static void netdev_rx(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct w90p910_rxbd *rxbd; - struct platform_device *pdev; - struct sk_buff *skb; - unsigned char *data; - unsigned int length, status, val, entry; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - rxbd = ðer->rdesc->desclist[ether->cur_rx]; - - do { - val = __raw_readl(ether->reg + REG_CRXDSA); - - entry = ether->rdesc_phys + - offsetof(struct recv_pdesc, desclist[ether->cur_rx]); - - if (val == entry) - break; - - status = rxbd->sl; - length = status & 0xFFFF; - - if (status & RXDS_RXGD) { - data = ether->rdesc->recv_buf[ether->cur_rx]; - skb = netdev_alloc_skb(dev, length + 2); - if (!skb) { - dev->stats.rx_dropped++; - return; - } - - skb_reserve(skb, 2); - skb_put(skb, length); - skb_copy_to_linear_data(skb, data, length); - skb->protocol = eth_type_trans(skb, dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += length; - netif_rx(skb); - } else { - dev->stats.rx_errors++; - - if (status & RXDS_RP) { - dev_err(&pdev->dev, "rx runt err\n"); - dev->stats.rx_length_errors++; - } else if (status & RXDS_CRCE) { - dev_err(&pdev->dev, "rx crc err\n"); - dev->stats.rx_crc_errors++; - } else if (status & RXDS_ALIE) { - dev_err(&pdev->dev, "rx alignment err\n"); - dev->stats.rx_frame_errors++; - } else if (status & RXDS_PTLE) { - dev_err(&pdev->dev, "rx longer err\n"); - dev->stats.rx_over_errors++; - } - } - - rxbd->sl = RX_OWEN_DMA; - rxbd->reserved = 0x0; - - if (++ether->cur_rx >= RX_DESC_SIZE) - ether->cur_rx = 0; - - rxbd = ðer->rdesc->desclist[ether->cur_rx]; - - } while (1); -} - -static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - struct w90p910_ether *ether; - struct platform_device *pdev; - unsigned int status; - - dev = dev_id; - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_get_and_clear_int(dev, &status); - - if (status & MISTA_RDU) { - netdev_rx(dev); - w90p910_trigger_rx(dev); - - return IRQ_HANDLED; - } else if (status & MISTA_RXBERR) { - dev_err(&pdev->dev, "emc rx bus error\n"); - w90p910_reset_mac(dev); - } - - netdev_rx(dev); - return IRQ_HANDLED; -} - -static int w90p910_ether_open(struct net_device *dev) -{ - struct w90p910_ether *ether; - struct platform_device *pdev; - - ether = netdev_priv(dev); - pdev = ether->pdev; - - w90p910_reset_mac(dev); - w90p910_set_fifo_threshold(dev); - w90p910_set_curdest(dev); - w90p910_enable_cam(dev); - w90p910_enable_cam_command(dev); - w90p910_enable_mac_interrupt(dev); - w90p910_set_global_maccmd(dev); - w90p910_enable_rx(dev, 1); - - clk_enable(ether->rmiiclk); - clk_enable(ether->clk); - - ether->rx_packets = 0x0; - ether->rx_bytes = 0x0; - - if (request_irq(ether->txirq, w90p910_tx_interrupt, - 0x0, pdev->name, dev)) { - dev_err(&pdev->dev, "register irq tx failed\n"); - return -EAGAIN; - } - - if (request_irq(ether->rxirq, w90p910_rx_interrupt, - 0x0, pdev->name, dev)) { - dev_err(&pdev->dev, "register irq rx failed\n"); - free_irq(ether->txirq, dev); - return -EAGAIN; - } - - mod_timer(ðer->check_timer, jiffies + msecs_to_jiffies(1000)); - netif_start_queue(dev); - w90p910_trigger_rx(dev); - - dev_info(&pdev->dev, "%s is OPENED\n", dev->name); - - return 0; -} - -static void w90p910_ether_set_multicast_list(struct net_device *dev) -{ - struct w90p910_ether *ether; - unsigned int rx_mode; - - ether = netdev_priv(dev); - - if (dev->flags & IFF_PROMISC) - rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; - else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev)) - rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP; - else - rx_mode = CAMCMR_ECMP | CAMCMR_ABP; - __raw_writel(rx_mode, ether->reg + REG_CAMCMR); -} - -static int w90p910_ether_ioctl(struct net_device *dev, - struct ifreq *ifr, int cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct mii_ioctl_data *data = if_mii(ifr); - - return generic_mii_ioctl(ðer->mii, data, cmd, NULL); -} - -static void w90p910_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); - strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); -} - -static int w90p910_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - mii_ethtool_get_link_ksettings(ðer->mii, cmd); - - return 0; -} - -static int w90p910_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_ethtool_set_link_ksettings(ðer->mii, cmd); -} - -static int w90p910_nway_reset(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_nway_restart(ðer->mii); -} - -static u32 w90p910_get_link(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - return mii_link_ok(ðer->mii); -} - -static const struct ethtool_ops w90p910_ether_ethtool_ops = { - .get_drvinfo = w90p910_get_drvinfo, - .nway_reset = w90p910_nway_reset, - .get_link = w90p910_get_link, - .get_link_ksettings = w90p910_get_link_ksettings, - .set_link_ksettings = w90p910_set_link_ksettings, -}; - -static const struct net_device_ops w90p910_ether_netdev_ops = { - .ndo_open = w90p910_ether_open, - .ndo_stop = w90p910_ether_close, - .ndo_start_xmit = w90p910_ether_start_xmit, - .ndo_set_rx_mode = w90p910_ether_set_multicast_list, - .ndo_set_mac_address = w90p910_set_mac_address, - .ndo_do_ioctl = w90p910_ether_ioctl, - .ndo_validate_addr = eth_validate_addr, -}; - -static void get_mac_address(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - struct platform_device *pdev; - char addr[ETH_ALEN]; - - pdev = ether->pdev; - - addr[0] = 0x00; - addr[1] = 0x02; - addr[2] = 0xac; - addr[3] = 0x55; - addr[4] = 0x88; - addr[5] = 0xa8; - - if (is_valid_ether_addr(addr)) - memcpy(dev->dev_addr, &addr, ETH_ALEN); - else - dev_err(&pdev->dev, "invalid mac address\n"); -} - -static int w90p910_ether_setup(struct net_device *dev) -{ - struct w90p910_ether *ether = netdev_priv(dev); - - dev->netdev_ops = &w90p910_ether_netdev_ops; - dev->ethtool_ops = &w90p910_ether_ethtool_ops; - - dev->tx_queue_len = 16; - dev->dma = 0x0; - dev->watchdog_timeo = TX_TIMEOUT; - - get_mac_address(dev); - - ether->cur_tx = 0x0; - ether->cur_rx = 0x0; - ether->finish_tx = 0x0; - ether->linkflag = 0x0; - ether->mii.phy_id = 0x01; - ether->mii.phy_id_mask = 0x1f; - ether->mii.reg_num_mask = 0x1f; - ether->mii.dev = dev; - ether->mii.mdio_read = w90p910_mdio_read; - ether->mii.mdio_write = w90p910_mdio_write; - - timer_setup(ðer->check_timer, w90p910_check_link, 0); - - return 0; -} - -static int w90p910_ether_probe(struct platform_device *pdev) -{ - struct w90p910_ether *ether; - struct net_device *dev; - int error; - - dev = alloc_etherdev(sizeof(struct w90p910_ether)); - if (!dev) - return -ENOMEM; - - ether = netdev_priv(dev); - - ether->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (ether->res == NULL) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - error = -ENXIO; - goto failed_free; - } - - if (!request_mem_region(ether->res->start, - resource_size(ether->res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto failed_free; - } - - ether->reg = ioremap(ether->res->start, resource_size(ether->res)); - if (ether->reg == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto failed_free_mem; - } - - ether->txirq = platform_get_irq(pdev, 0); - if (ether->txirq < 0) { - dev_err(&pdev->dev, "failed to get ether tx irq\n"); - error = -ENXIO; - goto failed_free_io; - } - - ether->rxirq = platform_get_irq(pdev, 1); - if (ether->rxirq < 0) { - dev_err(&pdev->dev, "failed to get ether rx irq\n"); - error = -ENXIO; - goto failed_free_io; - } - - platform_set_drvdata(pdev, dev); - - ether->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ether->clk)) { - dev_err(&pdev->dev, "failed to get ether clock\n"); - error = PTR_ERR(ether->clk); - goto failed_free_io; - } - - ether->rmiiclk = clk_get(&pdev->dev, "RMII"); - if (IS_ERR(ether->rmiiclk)) { - dev_err(&pdev->dev, "failed to get ether clock\n"); - error = PTR_ERR(ether->rmiiclk); - goto failed_put_clk; - } - - ether->pdev = pdev; - - w90p910_ether_setup(dev); - - error = register_netdev(dev); - if (error != 0) { - dev_err(&pdev->dev, "Register EMC w90p910 FAILED\n"); - error = -ENODEV; - goto failed_put_rmiiclk; - } - - return 0; -failed_put_rmiiclk: - clk_put(ether->rmiiclk); -failed_put_clk: - clk_put(ether->clk); -failed_free_io: - iounmap(ether->reg); -failed_free_mem: - release_mem_region(ether->res->start, resource_size(ether->res)); -failed_free: - free_netdev(dev); - return error; -} - -static int w90p910_ether_remove(struct platform_device *pdev) -{ - struct net_device *dev = platform_get_drvdata(pdev); - struct w90p910_ether *ether = netdev_priv(dev); - - unregister_netdev(dev); - - clk_put(ether->rmiiclk); - clk_put(ether->clk); - - iounmap(ether->reg); - release_mem_region(ether->res->start, resource_size(ether->res)); - - del_timer_sync(ðer->check_timer); - - free_netdev(dev); - return 0; -} - -static struct platform_driver w90p910_ether_driver = { - .probe = w90p910_ether_probe, - .remove = w90p910_ether_remove, - .driver = { - .name = "nuc900-emc", - }, -}; - -module_platform_driver(w90p910_ether_driver); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("w90p910 MAC driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-emc"); - From 305e503b14a879e619974b8e82236d5f32d0921e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:35 +0200 Subject: [PATCH 117/123] spi: remove w90x900 driver The ARM w90x900 platform is getting removed, so this driver is obsolete. Link: https://lore.kernel.org/r/20190809202749.742267-8-arnd@arndb.de Signed-off-by: Arnd Bergmann Acked-by: Mark Brown Signed-off-by: Arnd Bergmann --- drivers/spi/Kconfig | 7 - drivers/spi/Makefile | 1 - drivers/spi/spi-nuc900.c | 429 ----------------------- include/Kbuild | 1 - include/linux/platform_data/spi-nuc900.h | 29 -- 5 files changed, 467 deletions(-) delete mode 100644 drivers/spi/spi-nuc900.c delete mode 100644 include/linux/platform_data/spi-nuc900.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3a1d8f1170de..949b18ed9d6b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -440,13 +440,6 @@ config SPI_NPCM_PSPI This driver provides support for Nuvoton NPCM BMC Peripheral SPI controller in master mode. -config SPI_NUC900 - tristate "Nuvoton NUC900 series SPI" - depends on ARCH_W90X900 - select SPI_BITBANG - help - SPI driver for Nuvoton NUC900 series ARM SoCs - config SPI_LANTIQ_SSC tristate "Lantiq SSC SPI controller" depends on LANTIQ || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 63dcab552bcb..b1131809a294 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o -obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c deleted file mode 100644 index 37e2034ad4d5..000000000000 --- a/drivers/spi/spi-nuc900.c +++ /dev/null @@ -1,429 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2009 Nuvoton technology. - * Wan ZongShun - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -/* usi registers offset */ -#define USI_CNT 0x00 -#define USI_DIV 0x04 -#define USI_SSR 0x08 -#define USI_RX0 0x10 -#define USI_TX0 0x10 - -/* usi register bit */ -#define ENINT (0x01 << 17) -#define ENFLG (0x01 << 16) -#define SLEEP (0x0f << 12) -#define TXNUM (0x03 << 8) -#define TXBITLEN (0x1f << 3) -#define TXNEG (0x01 << 2) -#define RXNEG (0x01 << 1) -#define LSB (0x01 << 10) -#define SELECTLEV (0x01 << 2) -#define SELECTPOL (0x01 << 31) -#define SELECTSLAVE 0x01 -#define GOBUSY 0x01 - -struct nuc900_spi { - struct spi_bitbang bitbang; - struct completion done; - void __iomem *regs; - int irq; - int len; - int count; - const unsigned char *tx; - unsigned char *rx; - struct clk *clk; - struct spi_master *master; - struct nuc900_spi_info *pdata; - spinlock_t lock; -}; - -static inline struct nuc900_spi *to_hw(struct spi_device *sdev) -{ - return spi_master_get_devdata(sdev->master); -} - -static void nuc900_slave_select(struct spi_device *spi, unsigned int ssr) -{ - struct nuc900_spi *hw = to_hw(spi); - unsigned int val; - unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0; - unsigned int cpol = spi->mode & SPI_CPOL ? 1 : 0; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_SSR); - - if (!cs) - val &= ~SELECTLEV; - else - val |= SELECTLEV; - - if (!ssr) - val &= ~SELECTSLAVE; - else - val |= SELECTSLAVE; - - __raw_writel(val, hw->regs + USI_SSR); - - val = __raw_readl(hw->regs + USI_CNT); - - if (!cpol) - val &= ~SELECTPOL; - else - val |= SELECTPOL; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_spi_chipsel(struct spi_device *spi, int value) -{ - switch (value) { - case BITBANG_CS_INACTIVE: - nuc900_slave_select(spi, 0); - break; - - case BITBANG_CS_ACTIVE: - nuc900_slave_select(spi, 1); - break; - } -} - -static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, unsigned int txnum) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~TXNUM; - - if (txnum) - val |= txnum << 0x08; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); - -} - -static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw, - unsigned int txbitlen) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~TXBITLEN; - - val |= (txbitlen << 0x03); - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_spi_gobusy(struct nuc900_spi *hw) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - val |= GOBUSY; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) -{ - return hw->tx ? hw->tx[count] : 0; -} - -static int nuc900_spi_txrx(struct spi_device *spi, struct spi_transfer *t) -{ - struct nuc900_spi *hw = to_hw(spi); - - hw->tx = t->tx_buf; - hw->rx = t->rx_buf; - hw->len = t->len; - hw->count = 0; - - __raw_writel(hw_txbyte(hw, 0x0), hw->regs + USI_TX0); - - nuc900_spi_gobusy(hw); - - wait_for_completion(&hw->done); - - return hw->count; -} - -static irqreturn_t nuc900_spi_irq(int irq, void *dev) -{ - struct nuc900_spi *hw = dev; - unsigned int status; - unsigned int count = hw->count; - - status = __raw_readl(hw->regs + USI_CNT); - __raw_writel(status, hw->regs + USI_CNT); - - if (status & ENFLG) { - hw->count++; - - if (hw->rx) - hw->rx[count] = __raw_readl(hw->regs + USI_RX0); - count++; - - if (count < hw->len) { - __raw_writel(hw_txbyte(hw, count), hw->regs + USI_TX0); - nuc900_spi_gobusy(hw); - } else { - complete(&hw->done); - } - - return IRQ_HANDLED; - } - - complete(&hw->done); - return IRQ_HANDLED; -} - -static void nuc900_tx_edge(struct nuc900_spi *hw, unsigned int edge) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (edge) - val |= TXNEG; - else - val &= ~TXNEG; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_rx_edge(struct nuc900_spi *hw, unsigned int edge) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (edge) - val |= RXNEG; - else - val &= ~RXNEG; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_send_first(struct nuc900_spi *hw, unsigned int lsb) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - if (lsb) - val |= LSB; - else - val &= ~LSB; - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT) & ~SLEEP; - - if (sleep) - val |= (sleep << 12); - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_enable_int(struct nuc900_spi *hw) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&hw->lock, flags); - - val = __raw_readl(hw->regs + USI_CNT); - - val |= ENINT; - - __raw_writel(val, hw->regs + USI_CNT); - - spin_unlock_irqrestore(&hw->lock, flags); -} - -static void nuc900_set_divider(struct nuc900_spi *hw) -{ - __raw_writel(hw->pdata->divider, hw->regs + USI_DIV); -} - -static void nuc900_init_spi(struct nuc900_spi *hw) -{ - clk_enable(hw->clk); - spin_lock_init(&hw->lock); - - nuc900_tx_edge(hw, hw->pdata->txneg); - nuc900_rx_edge(hw, hw->pdata->rxneg); - nuc900_send_first(hw, hw->pdata->lsb); - nuc900_set_sleep(hw, hw->pdata->sleep); - nuc900_spi_setup_txbitlen(hw, hw->pdata->txbitlen); - nuc900_spi_setup_txnum(hw, hw->pdata->txnum); - nuc900_set_divider(hw); - nuc900_enable_int(hw); -} - -static int nuc900_spi_probe(struct platform_device *pdev) -{ - struct nuc900_spi *hw; - struct spi_master *master; - struct resource *res; - int err = 0; - - master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi)); - if (master == NULL) { - dev_err(&pdev->dev, "No memory for spi_master\n"); - return -ENOMEM; - } - - hw = spi_master_get_devdata(master); - hw->master = master; - hw->pdata = dev_get_platdata(&pdev->dev); - - if (hw->pdata == NULL) { - dev_err(&pdev->dev, "No platform data supplied\n"); - err = -ENOENT; - goto err_pdata; - } - - platform_set_drvdata(pdev, hw); - init_completion(&hw->done); - - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - if (hw->pdata->lsb) - master->mode_bits |= SPI_LSB_FIRST; - master->num_chipselect = hw->pdata->num_cs; - master->bus_num = hw->pdata->bus_num; - hw->bitbang.master = hw->master; - hw->bitbang.chipselect = nuc900_spi_chipsel; - hw->bitbang.txrx_bufs = nuc900_spi_txrx; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(hw->regs)) { - err = PTR_ERR(hw->regs); - goto err_pdata; - } - - hw->irq = platform_get_irq(pdev, 0); - if (hw->irq < 0) { - dev_err(&pdev->dev, "No IRQ specified\n"); - err = -ENOENT; - goto err_pdata; - } - - err = devm_request_irq(&pdev->dev, hw->irq, nuc900_spi_irq, 0, - pdev->name, hw); - if (err) { - dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_pdata; - } - - hw->clk = devm_clk_get(&pdev->dev, "spi"); - if (IS_ERR(hw->clk)) { - dev_err(&pdev->dev, "No clock for device\n"); - err = PTR_ERR(hw->clk); - goto err_pdata; - } - - mfp_set_groupg(&pdev->dev, NULL); - nuc900_init_spi(hw); - - err = spi_bitbang_start(&hw->bitbang); - if (err) { - dev_err(&pdev->dev, "Failed to register SPI master\n"); - goto err_register; - } - - return 0; - -err_register: - clk_disable(hw->clk); -err_pdata: - spi_master_put(hw->master); - return err; -} - -static int nuc900_spi_remove(struct platform_device *dev) -{ - struct nuc900_spi *hw = platform_get_drvdata(dev); - - spi_bitbang_stop(&hw->bitbang); - clk_disable(hw->clk); - spi_master_put(hw->master); - return 0; -} - -static struct platform_driver nuc900_spi_driver = { - .probe = nuc900_spi_probe, - .remove = nuc900_spi_remove, - .driver = { - .name = "nuc900-spi", - }, -}; -module_platform_driver(nuc900_spi_driver); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("nuc900 spi driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-spi"); diff --git a/include/Kbuild b/include/Kbuild index c38f0d46b267..5e0642d79dce 100644 --- a/include/Kbuild +++ b/include/Kbuild @@ -550,7 +550,6 @@ header-test- += linux/platform_data/sky81452-backlight.h header-test- += linux/platform_data/spi-davinci.h header-test- += linux/platform_data/spi-ep93xx.h header-test- += linux/platform_data/spi-mt65xx.h -header-test- += linux/platform_data/spi-nuc900.h header-test- += linux/platform_data/st_sensors_pdata.h header-test- += linux/platform_data/ti-sysc.h header-test- += linux/platform_data/timer-ixp4xx.h diff --git a/include/linux/platform_data/spi-nuc900.h b/include/linux/platform_data/spi-nuc900.h deleted file mode 100644 index ca3510877000..000000000000 --- a/include/linux/platform_data/spi-nuc900.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2009 Nuvoton technology corporation. - * - * Wan ZongShun - */ - -#ifndef __SPI_NUC900_H -#define __SPI_NUC900_H - -extern void mfp_set_groupg(struct device *dev, const char *subname); - -struct nuc900_spi_info { - unsigned int num_cs; - unsigned int lsb; - unsigned int txneg; - unsigned int rxneg; - unsigned int divider; - unsigned int sleep; - unsigned int txnum; - unsigned int txbitlen; - int bus_num; -}; - -struct nuc900_spi_chip { - unsigned char bits_per_word; -}; - -#endif /* __SPI_NUC900_H */ From c7b46e0c33c594623a279db4e1725d7ae477280f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:37 +0200 Subject: [PATCH 118/123] fbdev: remove w90x900/nuc900 platform drivers The ARM w90x900 platform is getting removed, so this driver is obsolete. Link: https://lore.kernel.org/r/20190809202749.742267-10-arnd@arndb.de Signed-off-by: Arnd Bergmann Acked-by: Bartlomiej Zolnierkiewicz Signed-off-by: Arnd Bergmann --- drivers/video/fbdev/Kconfig | 14 - drivers/video/fbdev/Makefile | 1 - drivers/video/fbdev/nuc900fb.c | 760 ------------------- drivers/video/fbdev/nuc900fb.h | 51 -- include/Kbuild | 1 - include/linux/platform_data/video-nuc900fb.h | 79 -- 6 files changed, 906 deletions(-) delete mode 100644 drivers/video/fbdev/nuc900fb.c delete mode 100644 drivers/video/fbdev/nuc900fb.h delete mode 100644 include/linux/platform_data/video-nuc900fb.h diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 6b2de93bd302..5f83cd715387 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1924,20 +1924,6 @@ config FB_S3C2410_DEBUG Turn on debugging messages. Note that you can set/unset at run time through sysfs -config FB_NUC900 - tristate "NUC900 LCD framebuffer support" - depends on FB && ARCH_W90X900 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - ---help--- - Frame buffer driver for the built-in LCD controller in the Nuvoton - NUC900 processor - -config GPM1040A0_320X240 - bool "Giantplus Technology GPM1040A0 320x240 Color TFT LCD" - depends on FB_NUC900 - config FB_SM501 tristate "Silicon Motion SM501 framebuffer support" depends on FB && MFD_SM501 diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 7dc4861a93e6..aab7155884ea 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -116,7 +116,6 @@ obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ -obj-$(CONFIG_FB_NUC900) += nuc900fb.o obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o obj-$(CONFIG_FB_PUV3_UNIGFX) += fb-puv3.o obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o diff --git a/drivers/video/fbdev/nuc900fb.c b/drivers/video/fbdev/nuc900fb.c deleted file mode 100644 index 4fd851598584..000000000000 --- a/drivers/video/fbdev/nuc900fb.c +++ /dev/null @@ -1,760 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * - * Copyright (c) 2009 Nuvoton technology corporation - * All rights reserved. - * - * Description: - * Nuvoton LCD Controller Driver - * Author: - * Wang Qiang (rurality.linux@gmail.com) 2009/12/11 - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "nuc900fb.h" - - -/* - * Initialize the nuc900 video (dual) buffer address - */ -static void nuc900fb_set_lcdaddr(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - unsigned long vbaddr1, vbaddr2; - - vbaddr1 = info->fix.smem_start; - vbaddr2 = info->fix.smem_start; - vbaddr2 += info->fix.line_length * info->var.yres; - - /* set frambuffer start phy addr*/ - writel(vbaddr1, regs + REG_LCM_VA_BADDR0); - writel(vbaddr2, regs + REG_LCM_VA_BADDR1); - - writel(fbi->regs.lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL); - writel(fbi->regs.lcd_va_scale, regs + REG_LCM_VA_SCALE); -} - -/* - * calculate divider for lcd div - */ -static unsigned int nuc900fb_calc_pixclk(struct nuc900fb_info *fbi, - unsigned long pixclk) -{ - unsigned long clk = fbi->clk_rate; - unsigned long long div; - - /* pixclk is in picseconds. our clock is in Hz*/ - /* div = (clk * pixclk)/10^12 */ - div = (unsigned long long)clk * pixclk; - div >>= 12; - do_div(div, 625 * 625UL * 625); - - dev_dbg(fbi->dev, "pixclk %ld, divisor is %lld\n", pixclk, div); - - return div; -} - -/* - * Check the video params of 'var'. - */ -static int nuc900fb_check_var(struct fb_var_screeninfo *var, - struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev); - struct nuc900fb_display *display = NULL; - struct nuc900fb_display *default_display = mach_info->displays + - mach_info->default_display; - int i; - - dev_dbg(fbi->dev, "check_var(var=%p, info=%p)\n", var, info); - - /* validate x/y resolution */ - /* choose default mode if possible */ - if (var->xres == default_display->xres && - var->yres == default_display->yres && - var->bits_per_pixel == default_display->bpp) - display = default_display; - else - for (i = 0; i < mach_info->num_displays; i++) - if (var->xres == mach_info->displays[i].xres && - var->yres == mach_info->displays[i].yres && - var->bits_per_pixel == mach_info->displays[i].bpp) { - display = mach_info->displays + i; - break; - } - - if (display == NULL) { - printk(KERN_ERR "wrong resolution or depth %dx%d at %d bit per pixel\n", - var->xres, var->yres, var->bits_per_pixel); - return -EINVAL; - } - - /* it should be the same size as the display */ - var->xres_virtual = display->xres; - var->yres_virtual = display->yres; - var->height = display->height; - var->width = display->width; - - /* copy lcd settings */ - var->pixclock = display->pixclock; - var->left_margin = display->left_margin; - var->right_margin = display->right_margin; - var->upper_margin = display->upper_margin; - var->lower_margin = display->lower_margin; - var->vsync_len = display->vsync_len; - var->hsync_len = display->hsync_len; - - var->transp.offset = 0; - var->transp.length = 0; - - fbi->regs.lcd_dccs = display->dccs; - fbi->regs.lcd_device_ctrl = display->devctl; - fbi->regs.lcd_va_fbctrl = display->fbctrl; - fbi->regs.lcd_va_scale = display->scale; - - /* set R/G/B possions */ - switch (var->bits_per_pixel) { - case 1: - case 2: - case 4: - case 8: - default: - var->red.offset = 0; - var->red.length = var->bits_per_pixel; - var->green = var->red; - var->blue = var->red; - break; - case 12: - var->red.length = 4; - var->green.length = 4; - var->blue.length = 4; - var->red.offset = 8; - var->green.offset = 4; - var->blue.offset = 0; - break; - case 16: - var->red.length = 5; - var->green.length = 6; - var->blue.length = 5; - var->red.offset = 11; - var->green.offset = 5; - var->blue.offset = 0; - break; - case 18: - var->red.length = 6; - var->green.length = 6; - var->blue.length = 6; - var->red.offset = 12; - var->green.offset = 6; - var->blue.offset = 0; - break; - case 32: - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->red.offset = 16; - var->green.offset = 8; - var->blue.offset = 0; - break; - } - - return 0; -} - -/* - * Calculate lcd register values from var setting & save into hw - */ -static void nuc900fb_calculate_lcd_regs(const struct fb_info *info, - struct nuc900fb_hw *regs) -{ - const struct fb_var_screeninfo *var = &info->var; - int vtt = var->height + var->upper_margin + var->lower_margin; - int htt = var->width + var->left_margin + var->right_margin; - int hsync = var->width + var->right_margin; - int vsync = var->height + var->lower_margin; - - regs->lcd_crtc_size = LCM_CRTC_SIZE_VTTVAL(vtt) | - LCM_CRTC_SIZE_HTTVAL(htt); - regs->lcd_crtc_dend = LCM_CRTC_DEND_VDENDVAL(var->height) | - LCM_CRTC_DEND_HDENDVAL(var->width); - regs->lcd_crtc_hr = LCM_CRTC_HR_EVAL(var->width + 5) | - LCM_CRTC_HR_SVAL(var->width + 1); - regs->lcd_crtc_hsync = LCM_CRTC_HSYNC_EVAL(hsync + var->hsync_len) | - LCM_CRTC_HSYNC_SVAL(hsync); - regs->lcd_crtc_vr = LCM_CRTC_VR_EVAL(vsync + var->vsync_len) | - LCM_CRTC_VR_SVAL(vsync); - -} - -/* - * Activate (set) the controller from the given framebuffer - * information - */ -static void nuc900fb_activate_var(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - struct fb_var_screeninfo *var = &info->var; - int clkdiv; - - clkdiv = nuc900fb_calc_pixclk(fbi, var->pixclock) - 1; - if (clkdiv < 0) - clkdiv = 0; - - nuc900fb_calculate_lcd_regs(info, &fbi->regs); - - /* set the new lcd registers*/ - - dev_dbg(fbi->dev, "new lcd register set:\n"); - dev_dbg(fbi->dev, "dccs = 0x%08x\n", fbi->regs.lcd_dccs); - dev_dbg(fbi->dev, "dev_ctl = 0x%08x\n", fbi->regs.lcd_device_ctrl); - dev_dbg(fbi->dev, "crtc_size = 0x%08x\n", fbi->regs.lcd_crtc_size); - dev_dbg(fbi->dev, "crtc_dend = 0x%08x\n", fbi->regs.lcd_crtc_dend); - dev_dbg(fbi->dev, "crtc_hr = 0x%08x\n", fbi->regs.lcd_crtc_hr); - dev_dbg(fbi->dev, "crtc_hsync = 0x%08x\n", fbi->regs.lcd_crtc_hsync); - dev_dbg(fbi->dev, "crtc_vr = 0x%08x\n", fbi->regs.lcd_crtc_vr); - - writel(fbi->regs.lcd_device_ctrl, regs + REG_LCM_DEV_CTRL); - writel(fbi->regs.lcd_crtc_size, regs + REG_LCM_CRTC_SIZE); - writel(fbi->regs.lcd_crtc_dend, regs + REG_LCM_CRTC_DEND); - writel(fbi->regs.lcd_crtc_hr, regs + REG_LCM_CRTC_HR); - writel(fbi->regs.lcd_crtc_hsync, regs + REG_LCM_CRTC_HSYNC); - writel(fbi->regs.lcd_crtc_vr, regs + REG_LCM_CRTC_VR); - - /* set lcd address pointers */ - nuc900fb_set_lcdaddr(info); - - writel(fbi->regs.lcd_dccs, regs + REG_LCM_DCCS); -} - -/* - * Alters the hardware state. - * - */ -static int nuc900fb_set_par(struct fb_info *info) -{ - struct fb_var_screeninfo *var = &info->var; - - switch (var->bits_per_pixel) { - case 32: - case 24: - case 18: - case 16: - case 12: - info->fix.visual = FB_VISUAL_TRUECOLOR; - break; - case 1: - info->fix.visual = FB_VISUAL_MONO01; - break; - default: - info->fix.visual = FB_VISUAL_PSEUDOCOLOR; - break; - } - - info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; - - /* activate this new configuration */ - nuc900fb_activate_var(info); - return 0; -} - -static inline unsigned int chan_to_field(unsigned int chan, - struct fb_bitfield *bf) -{ - chan &= 0xffff; - chan >>= 16 - bf->length; - return chan << bf->offset; -} - -static int nuc900fb_setcolreg(unsigned regno, - unsigned red, unsigned green, unsigned blue, - unsigned transp, struct fb_info *info) -{ - unsigned int val; - - switch (info->fix.visual) { - case FB_VISUAL_TRUECOLOR: - /* true-colour, use pseuo-palette */ - if (regno < 16) { - u32 *pal = info->pseudo_palette; - - val = chan_to_field(red, &info->var.red); - val |= chan_to_field(green, &info->var.green); - val |= chan_to_field(blue, &info->var.blue); - pal[regno] = val; - } - break; - - default: - return 1; /* unknown type */ - } - return 0; -} - -/** - * nuc900fb_blank - * - */ -static int nuc900fb_blank(int blank_mode, struct fb_info *info) -{ - - return 0; -} - -static struct fb_ops nuc900fb_ops = { - .owner = THIS_MODULE, - .fb_check_var = nuc900fb_check_var, - .fb_set_par = nuc900fb_set_par, - .fb_blank = nuc900fb_blank, - .fb_setcolreg = nuc900fb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, -}; - - -static inline void modify_gpio(void __iomem *reg, - unsigned long set, unsigned long mask) -{ - unsigned long tmp; - tmp = readl(reg) & ~mask; - writel(tmp | set, reg); -} - -/* - * Initialise LCD-related registers - */ -static int nuc900fb_init_registers(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev); - void __iomem *regs = fbi->io; - - /*reset the display engine*/ - writel(0, regs + REG_LCM_DCCS); - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_ENG_RST, - regs + REG_LCM_DCCS); - ndelay(100); - writel(readl(regs + REG_LCM_DCCS) & (~LCM_DCCS_ENG_RST), - regs + REG_LCM_DCCS); - ndelay(100); - - writel(0, regs + REG_LCM_DEV_CTRL); - - /* config gpio output */ - modify_gpio(W90X900_VA_GPIO + 0x54, mach_info->gpio_dir, - mach_info->gpio_dir_mask); - modify_gpio(W90X900_VA_GPIO + 0x58, mach_info->gpio_data, - mach_info->gpio_data_mask); - - return 0; -} - - -/* - * Alloc the SDRAM region of NUC900 for the frame buffer. - * The buffer should be a non-cached, non-buffered, memory region - * to allow palette and pixel writes without flushing the cache. - */ -static int nuc900fb_map_video_memory(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - dma_addr_t map_dma; - unsigned long map_size = PAGE_ALIGN(info->fix.smem_len); - - dev_dbg(fbi->dev, "nuc900fb_map_video_memory(fbi=%p) map_size %lu\n", - fbi, map_size); - - info->screen_base = dma_alloc_wc(fbi->dev, map_size, &map_dma, - GFP_KERNEL); - - if (!info->screen_base) - return -ENOMEM; - - memset(info->screen_base, 0x00, map_size); - info->fix.smem_start = map_dma; - - return 0; -} - -static inline void nuc900fb_unmap_video_memory(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len), - info->screen_base, info->fix.smem_start); -} - -static irqreturn_t nuc900fb_irqhandler(int irq, void *dev_id) -{ - struct nuc900fb_info *fbi = dev_id; - void __iomem *regs = fbi->io; - void __iomem *irq_base = fbi->irq_base; - unsigned long lcdirq = readl(regs + REG_LCM_INT_CS); - - if (lcdirq & LCM_INT_CS_DISP_F_STATUS) { - writel(readl(irq_base) | 1<<30, irq_base); - - /* wait VA_EN low */ - if ((readl(regs + REG_LCM_DCCS) & - LCM_DCCS_SINGLE) == LCM_DCCS_SINGLE) - while ((readl(regs + REG_LCM_DCCS) & - LCM_DCCS_VA_EN) == LCM_DCCS_VA_EN) - ; - /* display_out-enable */ - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN, - regs + REG_LCM_DCCS); - /* va-enable*/ - writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN, - regs + REG_LCM_DCCS); - } else if (lcdirq & LCM_INT_CS_UNDERRUN_INT) { - writel(readl(irq_base) | LCM_INT_CS_UNDERRUN_INT, irq_base); - } else if (lcdirq & LCM_INT_CS_BUS_ERROR_INT) { - writel(readl(irq_base) | LCM_INT_CS_BUS_ERROR_INT, irq_base); - } - - return IRQ_HANDLED; -} - -#ifdef CONFIG_CPU_FREQ - -static int nuc900fb_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct nuc900fb_info *info; - struct fb_info *fbinfo; - long delta_f; - info = container_of(nb, struct nuc900fb_info, freq_transition); - fbinfo = dev_get_drvdata(info->dev); - - delta_f = info->clk_rate - clk_get_rate(info->clk); - - if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) || - (val == CPUFREQ_PRECHANGE && delta_f < 0)) { - info->clk_rate = clk_get_rate(info->clk); - nuc900fb_activate_var(fbinfo); - } - - return 0; -} - -static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi) -{ - fbi->freq_transition.notifier_call = nuc900fb_cpufreq_transition; - return cpufreq_register_notifier(&fbi->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *fbi) -{ - cpufreq_unregister_notifier(&fbi->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} -#else -static inline int nuc900fb_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - return 0; -} - -static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi) -{ - return 0; -} - -static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *info) -{ -} -#endif - -static char driver_name[] = "nuc900fb"; - -static int nuc900fb_probe(struct platform_device *pdev) -{ - struct nuc900fb_info *fbi; - struct nuc900fb_display *display; - struct fb_info *fbinfo; - struct nuc900fb_mach_info *mach_info; - struct resource *res; - int ret; - int irq; - int i; - int size; - - dev_dbg(&pdev->dev, "devinit\n"); - mach_info = dev_get_platdata(&pdev->dev); - if (mach_info == NULL) { - dev_err(&pdev->dev, - "no platform data for lcd, cannot attach\n"); - return -EINVAL; - } - - if (mach_info->default_display > mach_info->num_displays) { - dev_err(&pdev->dev, - "default display No. is %d but only %d displays \n", - mach_info->default_display, mach_info->num_displays); - return -EINVAL; - } - - - display = mach_info->displays + mach_info->default_display; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq for device\n"); - return -ENOENT; - } - - fbinfo = framebuffer_alloc(sizeof(struct nuc900fb_info), &pdev->dev); - if (!fbinfo) - return -ENOMEM; - - platform_set_drvdata(pdev, fbinfo); - - fbi = fbinfo->par; - fbi->dev = &pdev->dev; - -#ifdef CONFIG_CPU_NUC950 - fbi->drv_type = LCDDRV_NUC950; -#endif - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - size = resource_size(res); - fbi->mem = request_mem_region(res->start, size, pdev->name); - if (fbi->mem == NULL) { - dev_err(&pdev->dev, "failed to alloc memory region\n"); - ret = -ENOENT; - goto free_fb; - } - - fbi->io = ioremap(res->start, size); - if (fbi->io == NULL) { - dev_err(&pdev->dev, "ioremap() of lcd registers failed\n"); - ret = -ENXIO; - goto release_mem_region; - } - - fbi->irq_base = fbi->io + REG_LCM_INT_CS; - - - /* Stop the LCD */ - writel(0, fbi->io + REG_LCM_DCCS); - - /* fill the fbinfo*/ - strcpy(fbinfo->fix.id, driver_name); - fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; - fbinfo->fix.type_aux = 0; - fbinfo->fix.xpanstep = 0; - fbinfo->fix.ypanstep = 0; - fbinfo->fix.ywrapstep = 0; - fbinfo->fix.accel = FB_ACCEL_NONE; - fbinfo->var.nonstd = 0; - fbinfo->var.activate = FB_ACTIVATE_NOW; - fbinfo->var.accel_flags = 0; - fbinfo->var.vmode = FB_VMODE_NONINTERLACED; - fbinfo->fbops = &nuc900fb_ops; - fbinfo->flags = FBINFO_FLAG_DEFAULT; - fbinfo->pseudo_palette = &fbi->pseudo_pal; - - ret = request_irq(irq, nuc900fb_irqhandler, 0, pdev->name, fbi); - if (ret) { - dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n", - irq, ret); - ret = -EBUSY; - goto release_regs; - } - - fbi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(fbi->clk)) { - printk(KERN_ERR "nuc900-lcd:failed to get lcd clock source\n"); - ret = PTR_ERR(fbi->clk); - goto release_irq; - } - - clk_enable(fbi->clk); - dev_dbg(&pdev->dev, "got and enabled clock\n"); - - fbi->clk_rate = clk_get_rate(fbi->clk); - - /* calutate the video buffer size */ - for (i = 0; i < mach_info->num_displays; i++) { - unsigned long smem_len = mach_info->displays[i].xres; - smem_len *= mach_info->displays[i].yres; - smem_len *= mach_info->displays[i].bpp; - smem_len >>= 3; - if (fbinfo->fix.smem_len < smem_len) - fbinfo->fix.smem_len = smem_len; - } - - /* Initialize Video Memory */ - ret = nuc900fb_map_video_memory(fbinfo); - if (ret) { - printk(KERN_ERR "Failed to allocate video RAM: %x\n", ret); - goto release_clock; - } - - dev_dbg(&pdev->dev, "got video memory\n"); - - fbinfo->var.xres = display->xres; - fbinfo->var.yres = display->yres; - fbinfo->var.bits_per_pixel = display->bpp; - - nuc900fb_init_registers(fbinfo); - - nuc900fb_check_var(&fbinfo->var, fbinfo); - - ret = nuc900fb_cpufreq_register(fbi); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register cpufreq\n"); - goto free_video_memory; - } - - ret = register_framebuffer(fbinfo); - if (ret) { - printk(KERN_ERR "failed to register framebuffer device: %d\n", - ret); - goto free_cpufreq; - } - - fb_info(fbinfo, "%s frame buffer device\n", fbinfo->fix.id); - - return 0; - -free_cpufreq: - nuc900fb_cpufreq_deregister(fbi); -free_video_memory: - nuc900fb_unmap_video_memory(fbinfo); -release_clock: - clk_disable(fbi->clk); - clk_put(fbi->clk); -release_irq: - free_irq(irq, fbi); -release_regs: - iounmap(fbi->io); -release_mem_region: - release_mem_region(res->start, size); -free_fb: - framebuffer_release(fbinfo); - return ret; -} - -/* - * shutdown the lcd controller - */ -static void nuc900fb_stop_lcd(struct fb_info *info) -{ - struct nuc900fb_info *fbi = info->par; - void __iomem *regs = fbi->io; - - writel((~LCM_DCCS_DISP_INT_EN) | (~LCM_DCCS_VA_EN) | (~LCM_DCCS_OSD_EN), - regs + REG_LCM_DCCS); -} - -/* - * Cleanup - */ -static int nuc900fb_remove(struct platform_device *pdev) -{ - struct fb_info *fbinfo = platform_get_drvdata(pdev); - struct nuc900fb_info *fbi = fbinfo->par; - int irq; - - nuc900fb_stop_lcd(fbinfo); - msleep(1); - - unregister_framebuffer(fbinfo); - nuc900fb_cpufreq_deregister(fbi); - nuc900fb_unmap_video_memory(fbinfo); - - iounmap(fbi->io); - - irq = platform_get_irq(pdev, 0); - free_irq(irq, fbi); - - release_resource(fbi->mem); - kfree(fbi->mem); - - framebuffer_release(fbinfo); - - return 0; -} - -#ifdef CONFIG_PM - -/* - * suspend and resume support for the lcd controller - */ - -static int nuc900fb_suspend(struct platform_device *dev, pm_message_t state) -{ - struct fb_info *fbinfo = platform_get_drvdata(dev); - struct nuc900fb_info *info = fbinfo->par; - - nuc900fb_stop_lcd(fbinfo); - msleep(1); - clk_disable(info->clk); - return 0; -} - -static int nuc900fb_resume(struct platform_device *dev) -{ - struct fb_info *fbinfo = platform_get_drvdata(dev); - struct nuc900fb_info *fbi = fbinfo->par; - - printk(KERN_INFO "nuc900fb resume\n"); - - clk_enable(fbi->clk); - msleep(1); - - nuc900fb_init_registers(fbinfo); - nuc900fb_activate_var(fbinfo); - - return 0; -} - -#else -#define nuc900fb_suspend NULL -#define nuc900fb_resume NULL -#endif - -static struct platform_driver nuc900fb_driver = { - .probe = nuc900fb_probe, - .remove = nuc900fb_remove, - .suspend = nuc900fb_suspend, - .resume = nuc900fb_resume, - .driver = { - .name = "nuc900-lcd", - }, -}; - -module_platform_driver(nuc900fb_driver); - -MODULE_DESCRIPTION("Framebuffer driver for the NUC900"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/nuc900fb.h b/drivers/video/fbdev/nuc900fb.h deleted file mode 100644 index 055ae9297931..000000000000 --- a/drivers/video/fbdev/nuc900fb.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * - * Copyright (c) 2009 Nuvoton technology corporation - * All rights reserved. - * - * Author: - * Wang Qiang(rurality.linux@gmail.com) 2009/12/16 - */ - -#ifndef __NUC900FB_H -#define __NUC900FB_H - -#include -#include - -enum nuc900_lcddrv_type { - LCDDRV_NUC910, - LCDDRV_NUC930, - LCDDRV_NUC932, - LCDDRV_NUC950, - LCDDRV_NUC960, -}; - - -#define PALETTE_BUFFER_SIZE 256 -#define PALETTE_BUFF_CLEAR (0x80000000) /* entry is clear/invalid */ - -struct nuc900fb_info { - struct device *dev; - struct clk *clk; - - struct resource *mem; - void __iomem *io; - void __iomem *irq_base; - int drv_type; - struct nuc900fb_hw regs; - unsigned long clk_rate; - -#ifdef CONFIG_CPU_FREQ - struct notifier_block freq_transition; -#endif - - /* keep these registers in case we need to re-write palette */ - u32 palette_buffer[PALETTE_BUFFER_SIZE]; - u32 pseudo_pal[16]; -}; - -int nuc900fb_init(void); - -#endif /* __NUC900FB_H */ diff --git a/include/Kbuild b/include/Kbuild index 5e0642d79dce..4d5a03a81fb5 100644 --- a/include/Kbuild +++ b/include/Kbuild @@ -568,7 +568,6 @@ header-test- += linux/platform_data/usb3503.h header-test- += linux/platform_data/ux500_wdt.h header-test- += linux/platform_data/video-clcd-versatile.h header-test- += linux/platform_data/video-imxfb.h -header-test- += linux/platform_data/video-nuc900fb.h header-test- += linux/platform_data/video-pxafb.h header-test- += linux/platform_data/video_s3c.h header-test- += linux/platform_data/voltage-omap.h diff --git a/include/linux/platform_data/video-nuc900fb.h b/include/linux/platform_data/video-nuc900fb.h deleted file mode 100644 index 3da504460c91..000000000000 --- a/include/linux/platform_data/video-nuc900fb.h +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* linux/include/asm/arch-nuc900/fb.h - * - * Copyright (c) 2008 Nuvoton technology corporation - * All rights reserved. - * - * Changelog: - * - * 2008/08/26 vincen.zswan modify this file for LCD. - */ - -#ifndef __ASM_ARM_FB_H -#define __ASM_ARM_FB_H - - - -/* LCD Controller Hardware Desc */ -struct nuc900fb_hw { - unsigned int lcd_dccs; - unsigned int lcd_device_ctrl; - unsigned int lcd_mpulcd_cmd; - unsigned int lcd_int_cs; - unsigned int lcd_crtc_size; - unsigned int lcd_crtc_dend; - unsigned int lcd_crtc_hr; - unsigned int lcd_crtc_hsync; - unsigned int lcd_crtc_vr; - unsigned int lcd_va_baddr0; - unsigned int lcd_va_baddr1; - unsigned int lcd_va_fbctrl; - unsigned int lcd_va_scale; - unsigned int lcd_va_test; - unsigned int lcd_va_win; - unsigned int lcd_va_stuff; -}; - -/* LCD Display Description */ -struct nuc900fb_display { - /* LCD Image type */ - unsigned type; - - /* LCD Screen Size */ - unsigned short width; - unsigned short height; - - /* LCD Screen Info */ - unsigned short xres; - unsigned short yres; - unsigned short bpp; - - unsigned long pixclock; - unsigned short left_margin; - unsigned short right_margin; - unsigned short hsync_len; - unsigned short upper_margin; - unsigned short lower_margin; - unsigned short vsync_len; - - /* hardware special register value */ - unsigned int dccs; - unsigned int devctl; - unsigned int fbctrl; - unsigned int scale; -}; - -struct nuc900fb_mach_info { - struct nuc900fb_display *displays; - unsigned num_displays; - unsigned default_display; - /* GPIO Setting Info */ - unsigned gpio_dir; - unsigned gpio_dir_mask; - unsigned gpio_data; - unsigned gpio_data_mask; -}; - -extern void __init nuc900_fb_set_platdata(struct nuc900fb_mach_info *); - -#endif /* __ASM_ARM_FB_H */ From 3b1261fb72c7dc6a9e4604ef1ea01d6bb67cc3d1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Sep 2019 15:19:56 +0200 Subject: [PATCH 119/123] bus: imx-weim: remove incorrect __init annotations The probe function is no longer __init, so anything it calls now must also be available at runtime, as Kbuild points out when building with clang-9: WARNING: vmlinux.o(.text+0x6e7040): Section mismatch in reference from the function weim_probe() to the function .init.text:imx_weim_gpr_setup() The function weim_probe() references the function __init imx_weim_gpr_setup(). This is often because weim_probe lacks a __init annotation or the annotation of imx_weim_gpr_setup is wrong. WARNING: vmlinux.o(.text+0x6e70f0): Section mismatch in reference from the function weim_probe() to the function .init.text:weim_timing_setup() The function weim_probe() references the function __init weim_timing_setup(). This is often because weim_probe lacks a __init annotation or the annotation of weim_timing_setup is wrong. Remove the remaining __init markings that are now wrong. Fixes: 4a92f07816ba ("bus: imx-weim: use module_platform_driver()") Signed-off-by: Arnd Bergmann --- drivers/bus/imx-weim.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 79af0c27f5a3..28bb65a5613f 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -76,7 +76,7 @@ static const struct of_device_id weim_id_table[] = { }; MODULE_DEVICE_TABLE(of, weim_id_table); -static int __init imx_weim_gpr_setup(struct platform_device *pdev) +static int imx_weim_gpr_setup(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct property *prop; @@ -126,10 +126,10 @@ err: } /* Parse and set the timing for this device. */ -static int __init weim_timing_setup(struct device *dev, - struct device_node *np, void __iomem *base, - const struct imx_weim_devtype *devtype, - struct cs_timing_state *ts) +static int weim_timing_setup(struct device *dev, + struct device_node *np, void __iomem *base, + const struct imx_weim_devtype *devtype, + struct cs_timing_state *ts) { u32 cs_idx, value[MAX_CS_REGS_COUNT]; int i, ret; From 45b659ee75d7bcff7d0206967d150d879f1d3eb9 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Mon, 26 Aug 2019 20:00:40 -0700 Subject: [PATCH 120/123] firmware: ti_sci: Allow for device shared and exclusive requests Sysfw provides an option for requesting exclusive access for a device using the flags MSG_FLAG_DEVICE_EXCLUSIVE. If this flag is not used, the device is meant to be shared across hosts. Once a device is requested from a host with this flag set, any request to this device from a different host will be nacked by sysfw. Current tisci driver enables this flag for every device requests. But this may not be true for all the devices. So provide a separate commands in driver for exclusive and shared device requests. Reviewed-by: Nishanth Menon Signed-off-by: Lokesh Vutla Signed-off-by: Santosh Shilimkar Signed-off-by: Arnd Bergmann --- drivers/firmware/ti_sci.c | 45 ++++++++++++++++++++++++-- include/linux/soc/ti/ti_sci_protocol.h | 3 ++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index cdee0b45943d..4126be9e3216 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -635,6 +635,7 @@ fail: /** * ti_sci_cmd_get_device() - command to request for device managed by TISCI + * that can be shared with other hosts. * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle * @id: Device Identifier * @@ -642,11 +643,29 @@ fail: * usage count by balancing get_device with put_device. No refcounting is * managed by driver for that purpose. * - * NOTE: The request is for exclusive access for the processor. - * * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) +{ + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_ON); +} + +/** + * ti_sci_cmd_get_device_exclusive() - command to request for device managed by + * TISCI that is exclusively owned by the + * requesting host. + * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle + * @id: Device Identifier + * + * Request for the device - NOTE: the client MUST maintain integrity of + * usage count by balancing get_device with put_device. No refcounting is + * managed by driver for that purpose. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_get_device_exclusive(const struct ti_sci_handle *handle, + u32 id) { return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, @@ -665,6 +684,26 @@ static int ti_sci_cmd_get_device(const struct ti_sci_handle *handle, u32 id) * Return: 0 if all went fine, else return appropriate error. */ static int ti_sci_cmd_idle_device(const struct ti_sci_handle *handle, u32 id) +{ + return ti_sci_set_device_state(handle, id, 0, + MSG_DEVICE_SW_STATE_RETENTION); +} + +/** + * ti_sci_cmd_idle_device_exclusive() - Command to idle a device managed by + * TISCI that is exclusively owned by + * requesting host. + * @handle: Pointer to TISCI handle as retrieved by *ti_sci_get_handle + * @id: Device Identifier + * + * Request for the device - NOTE: the client MUST maintain integrity of + * usage count by balancing get_device with put_device. No refcounting is + * managed by driver for that purpose. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_idle_device_exclusive(const struct ti_sci_handle *handle, + u32 id) { return ti_sci_set_device_state(handle, id, MSG_FLAG_DEVICE_EXCLUSIVE, @@ -2894,7 +2933,9 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) core_ops->reboot_device = ti_sci_cmd_core_reboot; dops->get_device = ti_sci_cmd_get_device; + dops->get_device_exclusive = ti_sci_cmd_get_device_exclusive; dops->idle_device = ti_sci_cmd_idle_device; + dops->idle_device_exclusive = ti_sci_cmd_idle_device_exclusive; dops->put_device = ti_sci_cmd_put_device; dops->is_valid = ti_sci_cmd_dev_is_valid; diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index 6c610e188a44..9531ec823298 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -97,7 +97,10 @@ struct ti_sci_core_ops { */ struct ti_sci_dev_ops { int (*get_device)(const struct ti_sci_handle *handle, u32 id); + int (*get_device_exclusive)(const struct ti_sci_handle *handle, u32 id); int (*idle_device)(const struct ti_sci_handle *handle, u32 id); + int (*idle_device_exclusive)(const struct ti_sci_handle *handle, + u32 id); int (*put_device)(const struct ti_sci_handle *handle, u32 id); int (*is_valid)(const struct ti_sci_handle *handle, u32 id); int (*get_context_loss_count)(const struct ti_sci_handle *handle, From 7a800c418c684372146f98f857d632caa30125f4 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Mon, 26 Aug 2019 20:00:40 -0700 Subject: [PATCH 121/123] dt-bindings: ti_sci_pm_domains: Add support for exclusive and shared access TISCI protocol supports for enabling the device either with exclusive permissions for the requesting host or with sharing across the hosts. There are certain devices which are exclusive to Linux context and there are certain devices that are shared across different host contexts. So add support for getting this information from DT by increasing the power-domain cells to 2. Acked-by: Tero Kristo Acked-by: Rob Herring Reviewed-by: Nishanth Menon Signed-off-by: Lokesh Vutla Signed-off-by: Santosh Shilimkar Signed-off-by: Arnd Bergmann --- .../devicetree/bindings/soc/ti/sci-pm-domain.txt | 11 +++++++++-- MAINTAINERS | 1 + include/dt-bindings/soc/ti,sci_pm_domain.h | 9 +++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 include/dt-bindings/soc/ti,sci_pm_domain.h diff --git a/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt b/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt index f7b00a7c0f68..f541d1f776a2 100644 --- a/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt +++ b/Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt @@ -19,8 +19,15 @@ child of the pmmc node. Required Properties: -------------------- - compatible: should be "ti,sci-pm-domain" -- #power-domain-cells: Must be 1 so that an id can be provided in each - device node. +- #power-domain-cells: Can be one of the following: + 1: Containing the device id of each node + 2: First entry should be device id + Second entry should be one of the floowing: + TI_SCI_PD_EXCLUSIVE: To allow device to be + exclusively controlled by + the requesting hosts. + TI_SCI_PD_SHARED: To allow device to be shared + by multiple hosts. Example (K2G): ------------- diff --git a/MAINTAINERS b/MAINTAINERS index ad386626c9fa..3ecca5d04e51 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15869,6 +15869,7 @@ F: drivers/firmware/ti_sci* F: include/linux/soc/ti/ti_sci_protocol.h F: Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt F: drivers/soc/ti/ti_sci_pm_domains.c +F: include/dt-bindings/soc/ti,sci_pm_domain.h F: Documentation/devicetree/bindings/reset/ti,sci-reset.txt F: Documentation/devicetree/bindings/clock/ti,sci-clk.txt F: drivers/clk/keystone/sci-clk.c diff --git a/include/dt-bindings/soc/ti,sci_pm_domain.h b/include/dt-bindings/soc/ti,sci_pm_domain.h new file mode 100644 index 000000000000..8f2a7360b65e --- /dev/null +++ b/include/dt-bindings/soc/ti,sci_pm_domain.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __DT_BINDINGS_TI_SCI_PM_DOMAIN_H +#define __DT_BINDINGS_TI_SCI_PM_DOMAIN_H + +#define TI_SCI_PD_EXCLUSIVE 1 +#define TI_SCI_PD_SHARED 0 + +#endif /* __DT_BINDINGS_TI_SCI_PM_DOMAIN_H */ From de8a618f30e8436b8b6f344604225990325fef91 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Mon, 26 Aug 2019 20:00:41 -0700 Subject: [PATCH 122/123] soc: ti: ti_sci_pm_domains: Add support for exclusive and shared access TISCI protocol supports for enabling the device either with exclusive permissions for the requesting host or with sharing across the hosts. There are certain devices which are exclusive to Linux context and there are certain devices that are shared across different host contexts. So add support for getting this information from DT by increasing the power-domain cells to 2. For keeping the DT backward compatibility intact, defaulting the device permissions to set the exclusive flag set. In this case the power-domain-cells is 1. Reviewed-by: Nishanth Menon Signed-off-by: Lokesh Vutla Signed-off-by: Santosh Shilimkar Signed-off-by: Arnd Bergmann --- drivers/soc/ti/ti_sci_pm_domains.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index 97817dd7ba24..8c2a2f23982c 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -15,15 +15,19 @@ #include #include #include +#include /** * struct ti_sci_genpd_dev_data: holds data needed for every device attached * to this genpd * @idx: index of the device that identifies it with the system * control processor. + * @exclusive: Permissions for exclusive request or shared request of the + * device. */ struct ti_sci_genpd_dev_data { int idx; + u8 exclusive; }; /** @@ -55,6 +59,14 @@ static int ti_sci_dev_id(struct device *dev) return sci_dev_data->idx; } +static u8 is_ti_sci_dev_exclusive(struct device *dev) +{ + struct generic_pm_domain_data *genpd_data = dev_gpd_data(dev); + struct ti_sci_genpd_dev_data *sci_dev_data = genpd_data->data; + + return sci_dev_data->exclusive; +} + /** * ti_sci_dev_to_sci_handle(): get pointer to ti_sci_handle * @dev: pointer to device associated with this genpd @@ -79,7 +91,10 @@ static int ti_sci_dev_start(struct device *dev) const struct ti_sci_handle *ti_sci = ti_sci_dev_to_sci_handle(dev); int idx = ti_sci_dev_id(dev); - return ti_sci->ops.dev_ops.get_device(ti_sci, idx); + if (is_ti_sci_dev_exclusive(dev)) + return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, idx); + else + return ti_sci->ops.dev_ops.get_device(ti_sci, idx); } /** @@ -110,7 +125,7 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, if (ret < 0) return ret; - if (pd_args.args_count != 1) + if (pd_args.args_count != 1 && pd_args.args_count != 2) return -EINVAL; idx = pd_args.args[0]; @@ -128,6 +143,10 @@ static int ti_sci_pd_attach_dev(struct generic_pm_domain *domain, return -ENOMEM; sci_dev_data->idx = idx; + /* Enable the exclusive permissions by default */ + sci_dev_data->exclusive = TI_SCI_PD_EXCLUSIVE; + if (pd_args.args_count == 2) + sci_dev_data->exclusive = pd_args.args[1] & 0x1; genpd_data = dev_gpd_data(dev); genpd_data->data = sci_dev_data; From ef92bfdaa7123bc8bdaed3f0f6e008500dd555da Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 5 Sep 2019 12:21:22 +0900 Subject: [PATCH 123/123] bus: uniphier-system-bus: use devm_platform_ioremap_resource() Replace the chain of platform_get_resource() and devm_ioremap_resource() with devm_platform_ioremap_resource(). This allows to remove the local variable for (struct resource *), and have one function call less. Link: https://lore.kernel.org/r/20190905032122.26076-1-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada Signed-off-by: Arnd Bergmann --- drivers/bus/uniphier-system-bus.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c index e845c1a93f21..f70dedace20b 100644 --- a/drivers/bus/uniphier-system-bus.c +++ b/drivers/bus/uniphier-system-bus.c @@ -176,7 +176,6 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct uniphier_system_bus_priv *priv; - struct resource *regs; const __be32 *ranges; u32 cells, addr, size; u64 paddr; @@ -186,8 +185,7 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->membase = devm_ioremap_resource(dev, regs); + priv->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->membase)) return PTR_ERR(priv->membase);