Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'baikal/drivers' into arm/drivers

[arnd: This is a patch series from Serge Semin to add a few drivers
that don't have any other subsystem maintainer tree to go through,
so I'm picking them up through the soc tree, full series description
from the mailing list below]

Baikal-T1 SoC CPU is based on two MIPS Warrior P5600 cores. Their main
memory Non-Coherent IO interface is connected to the OCP2AXI bridge,
which in turn is then connected to the DW AMBA 3 AXI Interconnect (so
called Main Interconnect) with nine masters and four slaves ports. Main
Interconnect is responsible for the AXI-bus traffic arbitration (QoS)
and its routing from one component to another. In addition there is
a Errors Handler Block (EHB) accesible by means of the Baikal-T1 SoC
System Controller responsible to detect AXI protocol errors and device
not responding situations built on top the interconnect. Baikal-T1 AXI-bus
driver included in this patchset will be responsible for working with that
functionality, though currently it doesn't support QoS tuning. Instead
it's capable of detecting the error events, reporting an info about
them to the system log, injecting artificial errors to test the driver
functionality. Since AXI Interconnect doesn't provide a way to find
out which devices are connected to it, so its DT node is supposed to
be compatible with "simple-bus" driver, while sub-nodes shall represent
the masters attached to the bus.

One of the AXI Interconnect slaves is an AXI-APB bridge used to access the
Baikal-T1 SoC subsystems CSRs. MMIO request from CPU and DMAC masters are
routed there if they are detected to be within [0x08000000 0x1FFFFFFF]
range of the physical memory. In case if an attempted APB transaction
stays with no response for a pre-defined time it will be detected by
the APB-bus Errors Handler Block (EHB), which will raise an interrupt,
then the bus gets freed for a next operation. The APB-bus driver provides
the interrupt handler to detect the erroneous address, update an errors
counter and prints an error message about the faulty address. The counter
and the APB-bus operations timeout can be accessed via corresponding sysfs
nodes. A dedicated sysfs-node can be also used to artificially cause the
bus errors described above. Since APB-bus is a platform bus, it doesn't
provide a way to detect slave devices connected to it, so similarly to
the AXI-bus it's also supposed to be compatible with "simple-bus" driver.

Aside from PCIe/SATA/DDR/I2C/EHB/CPU/reboot specific settings the
Baikal-T1 System Controller provides a MIPS P5600 CM2 L2-cache tuning
block. It is responsible for the setting up the Tag/Data/WS L2-to-RAM
latencies. The last small patch in this patchset provides a driver and
DT-schema-based binding for the described device. So that the latencies
can be tuned up by means of dedicated DT properties and sysfs nodes.

This patchset is rebased and tested on the mainline Linux kernel
5.7-rc4.

Changelog v2 (AXI/APB bus):
- Assign dual GPL/BSD licenses to the bindings.
- Use single lined copyright headers in the bindings.
- Replace "additionalProperties: false" property with
"unevaluatedProperties: false" in the bindings.
- Don't use a multi-arg clock phandle reference in DT binding examples.
Thus remove includes from there.
- Fix some commit message and Kconfig help text spelling.
- Move drivers from soc to the bus subsystem.
- Convert a simple EHB drivers to the Baikal-T1 AXI and APB bus ones.
- Convert APB bus driver to using regmap MMIO API.
- Use syscon regmap to access the AXI-bus erroneous address.
- Add reset line support.
- Add Main Interconnect clock support to the AXI-bus driver.
- Remove probe-status info string printout.
- Discard of_match_ptr() macro utilization.
- Don't print error-message if no platform IRQ found. Just return an
error.
- Use generic FIELD_{GET,PREP} macros instead of handwritten ones in the
AXI-bus driver.

Changelog v2 (l2 driver):
- Fix some commit message and Kconfig help text spelling.
- Move the driver to the memory subsystem.
- Assign dual GPL/BSD license to the DT binding.
- Use single lined copyright header in the binding.
- Discard reg property and syscon compatible string.
- Move "allOf" restrictions to the root level of the properties.
- The DT node is supposed to be a child of the Baikal-T1 system
controller node. So regmap will be fetched from there.
- Use generic FIELD_{GET,PREP} macro.
- Remove probe-status info string printout.
- Since the driver depends on the OF config we can remove of_match_ptr()
macro utilization.

Changelog v3:
- Combine l2 and AXI/APB bus patches in a single patchset.
- Retrieve AXI-bus QoS registers by resource name "qos".
- Discard CONFIG_OF dependency since there is none at compile-time.
- Add syscon EHB registers range to the AXI-bus reg property as optional
entry.
- Fix invalid of_property_read_u32() return value test in the l2-ctl
driver.
- Get the reg property back into the l2-ctl DT bindings even though the
driver is using the parental syscon regmap.
- The l2-ctl DT schema will live separately from the system controller,
but the corresponding sub-node of the later DT schema will $ref this
one.
- Set non-default latencies in the l2-ctl DT example.

* baikal/drivers:
memory: Add Baikal-T1 L2-cache Control Block driver
bus: Add Baikal-T1 APB-bus driver
bus: Add Baikal-T1 AXI-bus driver
dt-bindings: bus: Add Baikal-T1 APB-bus binding
dt-bindings: bus: Add Baikal-T1 AXI-bus binding

Link: https://lore.kernel.org/lkml/20200526130841.ap6qlxv7hqmabnh5@mobilestation/
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

+1305
+90
Documentation/devicetree/bindings/bus/baikal,bt1-apb.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + # Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 3 + %YAML 1.2 4 + --- 5 + $id: http://devicetree.org/schemas/bus/baikal,bt1-apb.yaml# 6 + $schema: http://devicetree.org/meta-schemas/core.yaml# 7 + 8 + title: Baikal-T1 APB-bus 9 + 10 + maintainers: 11 + - Serge Semin <fancer.lancer@gmail.com> 12 + 13 + description: | 14 + Baikal-T1 CPU or DMAC MMIO requests are handled by the AMBA 3 AXI Interconnect 15 + which routes them to the AXI-APB bridge. This interface is a single master 16 + multiple slaves bus in turn serializing IO accesses and routing them to the 17 + addressed APB slave devices. In case of any APB protocol collisions, slave 18 + device not responding on timeout an IRQ is raised with an erroneous address 19 + reported to the APB terminator (APB Errors Handler Block). 20 + 21 + allOf: 22 + - $ref: /schemas/simple-bus.yaml# 23 + 24 + properties: 25 + compatible: 26 + contains: 27 + const: baikal,bt1-apb 28 + 29 + reg: 30 + items: 31 + - description: APB EHB MMIO registers 32 + - description: APB MMIO region with no any device mapped 33 + 34 + reg-names: 35 + items: 36 + - const: ehb 37 + - const: nodev 38 + 39 + interrupts: 40 + maxItems: 1 41 + 42 + clocks: 43 + items: 44 + - description: APB reference clock 45 + 46 + clock-names: 47 + items: 48 + - const: pclk 49 + 50 + resets: 51 + items: 52 + - description: APB domain reset line 53 + 54 + reset-names: 55 + items: 56 + - const: prst 57 + 58 + unevaluatedProperties: false 59 + 60 + required: 61 + - compatible 62 + - reg 63 + - reg-names 64 + - interrupts 65 + - clocks 66 + - clock-names 67 + 68 + examples: 69 + - | 70 + #include <dt-bindings/interrupt-controller/mips-gic.h> 71 + 72 + bus@1f059000 { 73 + compatible = "baikal,bt1-apb", "simple-bus"; 74 + reg = <0 0x1f059000 0 0x1000>, 75 + <0 0x1d000000 0 0x2040000>; 76 + reg-names = "ehb", "nodev"; 77 + #address-cells = <1>; 78 + #size-cells = <1>; 79 + 80 + ranges; 81 + 82 + interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>; 83 + 84 + clocks = <&ccu_sys 1>; 85 + clock-names = "pclk"; 86 + 87 + resets = <&ccu_sys 1>; 88 + reset-names = "prst"; 89 + }; 90 + ...
+107
Documentation/devicetree/bindings/bus/baikal,bt1-axi.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + # Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 3 + %YAML 1.2 4 + --- 5 + $id: http://devicetree.org/schemas/bus/baikal,bt1-axi.yaml# 6 + $schema: http://devicetree.org/meta-schemas/core.yaml# 7 + 8 + title: Baikal-T1 AXI-bus 9 + 10 + maintainers: 11 + - Serge Semin <fancer.lancer@gmail.com> 12 + 13 + description: | 14 + AXI3-bus is the main communication bus of Baikal-T1 SoC connecting all 15 + high-speed peripheral IP-cores with RAM controller and with MIPS P5600 16 + cores. Traffic arbitration is done by means of DW AXI Interconnect (so 17 + called AXI Main Interconnect) routing IO requests from one block to 18 + another: from CPU to SoC peripherals and between some SoC peripherals 19 + (mostly between peripheral devices and RAM, but also between DMA and 20 + some peripherals). In case of any protocol error, device not responding 21 + an IRQ is raised and a faulty situation is reported to the AXI EHB 22 + (Errors Handler Block) embedded on top of the DW AXI Interconnect and 23 + accessible by means of the Baikal-T1 System Controller. 24 + 25 + allOf: 26 + - $ref: /schemas/simple-bus.yaml# 27 + 28 + properties: 29 + compatible: 30 + contains: 31 + const: baikal,bt1-axi 32 + 33 + reg: 34 + minItems: 1 35 + items: 36 + - description: Synopsys DesignWare AXI Interconnect QoS registers 37 + - description: AXI EHB MMIO system controller registers 38 + 39 + reg-names: 40 + minItems: 1 41 + items: 42 + - const: qos 43 + - const: ehb 44 + 45 + '#interconnect-cells': 46 + const: 1 47 + 48 + syscon: 49 + $ref: /schemas/types.yaml#definitions/phandle 50 + description: Phandle to the Baikal-T1 System Controller DT node 51 + 52 + interrupts: 53 + maxItems: 1 54 + 55 + clocks: 56 + items: 57 + - description: Main Interconnect uplink reference clock 58 + 59 + clock-names: 60 + items: 61 + - const: aclk 62 + 63 + resets: 64 + items: 65 + - description: Main Interconnect reset line 66 + 67 + reset-names: 68 + items: 69 + - const: arst 70 + 71 + unevaluatedProperties: false 72 + 73 + required: 74 + - compatible 75 + - reg 76 + - reg-names 77 + - syscon 78 + - interrupts 79 + - clocks 80 + - clock-names 81 + 82 + examples: 83 + - | 84 + #include <dt-bindings/interrupt-controller/mips-gic.h> 85 + 86 + bus@1f05a000 { 87 + compatible = "baikal,bt1-axi", "simple-bus"; 88 + reg = <0 0x1f05a000 0 0x1000>, 89 + <0 0x1f04d110 0 0x8>; 90 + reg-names = "qos", "ehb"; 91 + #address-cells = <1>; 92 + #size-cells = <1>; 93 + #interconnect-cells = <1>; 94 + 95 + syscon = <&syscon>; 96 + 97 + ranges; 98 + 99 + interrupts = <GIC_SHARED 127 IRQ_TYPE_LEVEL_HIGH>; 100 + 101 + clocks = <&ccu_axi 0>; 102 + clock-names = "aclk"; 103 + 104 + resets = <&ccu_axi 0>; 105 + reset-names = "arst"; 106 + }; 107 + ...
+30
drivers/bus/Kconfig
··· 29 29 arbiter. This driver provides timeout and target abort error handling 30 30 and internal bus master decoding. 31 31 32 + config BT1_APB 33 + tristate "Baikal-T1 APB-bus driver" 34 + depends on MIPS_BAIKAL_T1 || COMPILE_TEST 35 + select REGMAP_MMIO 36 + help 37 + Baikal-T1 AXI-APB bridge is used to access the SoC subsystem CSRs. 38 + IO requests are routed to this bus by means of the DW AMBA 3 AXI 39 + Interconnect. In case of any APB protocol collisions, slave device 40 + not responding on timeout an IRQ is raised with an erroneous address 41 + reported to the APB terminator (APB Errors Handler Block). This 42 + driver provides the interrupt handler to detect the erroneous 43 + address, prints an error message about the address fault, updates an 44 + errors counter. The counter and the APB-bus operations timeout can be 45 + accessed via corresponding sysfs nodes. 46 + 47 + config BT1_AXI 48 + tristate "Baikal-T1 AXI-bus driver" 49 + depends on MIPS_BAIKAL_T1 || COMPILE_TEST 50 + select MFD_SYSCON 51 + help 52 + AXI3-bus is the main communication bus connecting all high-speed 53 + peripheral IP-cores with RAM controller and with MIPS P5600 cores on 54 + Baikal-T1 SoC. Traffic arbitration is done by means of DW AMBA 3 AXI 55 + Interconnect (so called AXI Main Interconnect) routing IO requests 56 + from one SoC block to another. This driver provides a way to detect 57 + any bus protocol errors and device not responding situations by 58 + means of an embedded on top of the interconnect errors handler 59 + block (EHB). AXI Interconnect QoS arbitration tuning is currently 60 + unsupported. 61 + 32 62 config MOXTET 33 63 tristate "CZ.NIC Turris Mox module configuration bus" 34 64 depends on SPI_MASTER && OF
+2
drivers/bus/Makefile
··· 13 13 # DPAA2 fsl-mc bus 14 14 obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ 15 15 16 + obj-$(CONFIG_BT1_APB) += bt1-apb.o 17 + obj-$(CONFIG_BT1_AXI) += bt1-axi.o 16 18 obj-$(CONFIG_IMX_WEIM) += imx-weim.o 17 19 obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o 18 20 obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
+422
drivers/bus/bt1-apb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4 + * 5 + * Authors: 6 + * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7 + * 8 + * Baikal-T1 APB-bus driver 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/types.h> 14 + #include <linux/device.h> 15 + #include <linux/atomic.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/interrupt.h> 18 + #include <linux/nmi.h> 19 + #include <linux/of.h> 20 + #include <linux/regmap.h> 21 + #include <linux/clk.h> 22 + #include <linux/reset.h> 23 + #include <linux/time64.h> 24 + #include <linux/clk.h> 25 + #include <linux/sysfs.h> 26 + 27 + #define APB_EHB_ISR 0x00 28 + #define APB_EHB_ISR_PENDING BIT(0) 29 + #define APB_EHB_ISR_MASK BIT(1) 30 + #define APB_EHB_ADDR 0x04 31 + #define APB_EHB_TIMEOUT 0x08 32 + 33 + #define APB_EHB_TIMEOUT_MIN 0x000003FFU 34 + #define APB_EHB_TIMEOUT_MAX 0xFFFFFFFFU 35 + 36 + /* 37 + * struct bt1_apb - Baikal-T1 APB EHB private data 38 + * @dev: Pointer to the device structure. 39 + * @regs: APB EHB registers map. 40 + * @res: No-device error injection memory region. 41 + * @irq: Errors IRQ number. 42 + * @rate: APB-bus reference clock rate. 43 + * @pclk: APB-reference clock. 44 + * @prst: APB domain reset line. 45 + * @count: Number of errors detected. 46 + */ 47 + struct bt1_apb { 48 + struct device *dev; 49 + 50 + struct regmap *regs; 51 + void __iomem *res; 52 + int irq; 53 + 54 + unsigned long rate; 55 + struct clk *pclk; 56 + 57 + struct reset_control *prst; 58 + 59 + atomic_t count; 60 + }; 61 + 62 + static const struct regmap_config bt1_apb_regmap_cfg = { 63 + .reg_bits = 32, 64 + .val_bits = 32, 65 + .reg_stride = 4, 66 + .max_register = APB_EHB_TIMEOUT, 67 + .fast_io = true 68 + }; 69 + 70 + static inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n) 71 + { 72 + u64 timeout = (u64)n * USEC_PER_SEC; 73 + 74 + do_div(timeout, apb->rate); 75 + 76 + return timeout; 77 + 78 + } 79 + 80 + static inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb, 81 + unsigned long timeout) 82 + { 83 + u64 n = (u64)timeout * apb->rate; 84 + 85 + do_div(n, USEC_PER_SEC); 86 + 87 + return n; 88 + 89 + } 90 + 91 + static irqreturn_t bt1_apb_isr(int irq, void *data) 92 + { 93 + struct bt1_apb *apb = data; 94 + u32 addr = 0; 95 + 96 + regmap_read(apb->regs, APB_EHB_ADDR, &addr); 97 + 98 + dev_crit_ratelimited(apb->dev, 99 + "APB-bus fault %d: Slave access timeout at 0x%08x\n", 100 + atomic_inc_return(&apb->count), 101 + addr); 102 + 103 + /* 104 + * Print backtrace on each CPU. This might be pointless if the fault 105 + * has happened on the same CPU as the IRQ handler is executed or 106 + * the other core proceeded further execution despite the error. 107 + * But if it's not, by looking at the trace we would get straight to 108 + * the cause of the problem. 109 + */ 110 + trigger_all_cpu_backtrace(); 111 + 112 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0); 113 + 114 + return IRQ_HANDLED; 115 + } 116 + 117 + static void bt1_apb_clear_data(void *data) 118 + { 119 + struct bt1_apb *apb = data; 120 + struct platform_device *pdev = to_platform_device(apb->dev); 121 + 122 + platform_set_drvdata(pdev, NULL); 123 + } 124 + 125 + static struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev) 126 + { 127 + struct device *dev = &pdev->dev; 128 + struct bt1_apb *apb; 129 + int ret; 130 + 131 + apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL); 132 + if (!apb) 133 + return ERR_PTR(-ENOMEM); 134 + 135 + ret = devm_add_action(dev, bt1_apb_clear_data, apb); 136 + if (ret) { 137 + dev_err(dev, "Can't add APB EHB data clear action\n"); 138 + return ERR_PTR(ret); 139 + } 140 + 141 + apb->dev = dev; 142 + atomic_set(&apb->count, 0); 143 + platform_set_drvdata(pdev, apb); 144 + 145 + return apb; 146 + } 147 + 148 + static int bt1_apb_request_regs(struct bt1_apb *apb) 149 + { 150 + struct platform_device *pdev = to_platform_device(apb->dev); 151 + void __iomem *regs; 152 + 153 + regs = devm_platform_ioremap_resource_byname(pdev, "ehb"); 154 + if (IS_ERR(regs)) { 155 + dev_err(apb->dev, "Couldn't map APB EHB registers\n"); 156 + return PTR_ERR(regs); 157 + } 158 + 159 + apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg); 160 + if (IS_ERR(apb->regs)) { 161 + dev_err(apb->dev, "Couldn't create APB EHB regmap\n"); 162 + return PTR_ERR(apb->regs); 163 + } 164 + 165 + apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev"); 166 + if (IS_ERR(apb->res)) { 167 + dev_err(apb->dev, "Couldn't map reserved region\n"); 168 + return PTR_ERR(apb->res); 169 + } 170 + 171 + return 0; 172 + } 173 + 174 + static int bt1_apb_request_rst(struct bt1_apb *apb) 175 + { 176 + int ret; 177 + 178 + apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst"); 179 + if (IS_ERR(apb->prst)) { 180 + dev_warn(apb->dev, "Couldn't get reset control line\n"); 181 + return PTR_ERR(apb->prst); 182 + } 183 + 184 + ret = reset_control_deassert(apb->prst); 185 + if (ret) 186 + dev_err(apb->dev, "Failed to deassert the reset line\n"); 187 + 188 + return ret; 189 + } 190 + 191 + static void bt1_apb_disable_clk(void *data) 192 + { 193 + struct bt1_apb *apb = data; 194 + 195 + clk_disable_unprepare(apb->pclk); 196 + } 197 + 198 + static int bt1_apb_request_clk(struct bt1_apb *apb) 199 + { 200 + int ret; 201 + 202 + apb->pclk = devm_clk_get(apb->dev, "pclk"); 203 + if (IS_ERR(apb->pclk)) { 204 + dev_err(apb->dev, "Couldn't get APB clock descriptor\n"); 205 + return PTR_ERR(apb->pclk); 206 + } 207 + 208 + ret = clk_prepare_enable(apb->pclk); 209 + if (ret) { 210 + dev_err(apb->dev, "Couldn't enable the APB clock\n"); 211 + return ret; 212 + } 213 + 214 + ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb); 215 + if (ret) { 216 + dev_err(apb->dev, "Can't add APB EHB clocks disable action\n"); 217 + return ret; 218 + } 219 + 220 + apb->rate = clk_get_rate(apb->pclk); 221 + if (!apb->rate) { 222 + dev_err(apb->dev, "Invalid clock rate\n"); 223 + return -EINVAL; 224 + } 225 + 226 + return 0; 227 + } 228 + 229 + static void bt1_apb_clear_irq(void *data) 230 + { 231 + struct bt1_apb *apb = data; 232 + 233 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0); 234 + } 235 + 236 + static int bt1_apb_request_irq(struct bt1_apb *apb) 237 + { 238 + struct platform_device *pdev = to_platform_device(apb->dev); 239 + int ret; 240 + 241 + apb->irq = platform_get_irq(pdev, 0); 242 + if (apb->irq < 0) 243 + return apb->irq; 244 + 245 + ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED, 246 + "bt1-apb", apb); 247 + if (ret) { 248 + dev_err(apb->dev, "Couldn't request APB EHB IRQ\n"); 249 + return ret; 250 + } 251 + 252 + ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb); 253 + if (ret) { 254 + dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n"); 255 + return ret; 256 + } 257 + 258 + /* Unmask IRQ and clear it' pending flag. */ 259 + regmap_update_bits(apb->regs, APB_EHB_ISR, 260 + APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK, 261 + APB_EHB_ISR_MASK); 262 + 263 + return 0; 264 + } 265 + 266 + static ssize_t count_show(struct device *dev, struct device_attribute *attr, 267 + char *buf) 268 + { 269 + struct bt1_apb *apb = dev_get_drvdata(dev); 270 + 271 + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count)); 272 + } 273 + static DEVICE_ATTR_RO(count); 274 + 275 + static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, 276 + char *buf) 277 + { 278 + struct bt1_apb *apb = dev_get_drvdata(dev); 279 + unsigned long timeout; 280 + int ret; 281 + u32 n; 282 + 283 + ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n); 284 + if (ret) 285 + return ret; 286 + 287 + timeout = bt1_apb_n_to_timeout_us(apb, n); 288 + 289 + return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout); 290 + } 291 + 292 + static ssize_t timeout_store(struct device *dev, 293 + struct device_attribute *attr, 294 + const char *buf, size_t count) 295 + { 296 + struct bt1_apb *apb = dev_get_drvdata(dev); 297 + unsigned long timeout; 298 + int ret; 299 + u32 n; 300 + 301 + if (kstrtoul(buf, 0, &timeout) < 0) 302 + return -EINVAL; 303 + 304 + n = bt1_apb_timeout_to_n_us(apb, timeout); 305 + n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX); 306 + 307 + ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n); 308 + 309 + return ret ?: count; 310 + } 311 + static DEVICE_ATTR_RW(timeout); 312 + 313 + static ssize_t inject_error_show(struct device *dev, struct device_attribute *attr, 314 + char *buf) 315 + { 316 + return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n"); 317 + } 318 + 319 + static ssize_t inject_error_store(struct device *dev, 320 + struct device_attribute *attr, 321 + const char *data, size_t count) 322 + { 323 + struct bt1_apb *apb = dev_get_drvdata(dev); 324 + 325 + /* 326 + * Either dummy read from the unmapped address in the APB IO area 327 + * or manually set the IRQ status. 328 + */ 329 + if (!strncmp(data, "nodev", 5)) 330 + readl(apb->res); 331 + else if (!strncmp(data, "irq", 3)) 332 + regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 333 + APB_EHB_ISR_PENDING); 334 + else 335 + return -EINVAL; 336 + 337 + return count; 338 + } 339 + static DEVICE_ATTR_RW(inject_error); 340 + 341 + static struct attribute *bt1_apb_sysfs_attrs[] = { 342 + &dev_attr_count.attr, 343 + &dev_attr_timeout.attr, 344 + &dev_attr_inject_error.attr, 345 + NULL 346 + }; 347 + ATTRIBUTE_GROUPS(bt1_apb_sysfs); 348 + 349 + static void bt1_apb_remove_sysfs(void *data) 350 + { 351 + struct bt1_apb *apb = data; 352 + 353 + device_remove_groups(apb->dev, bt1_apb_sysfs_groups); 354 + } 355 + 356 + static int bt1_apb_init_sysfs(struct bt1_apb *apb) 357 + { 358 + int ret; 359 + 360 + ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups); 361 + if (ret) { 362 + dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n"); 363 + return ret; 364 + } 365 + 366 + ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb); 367 + if (ret) 368 + dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n"); 369 + 370 + return ret; 371 + } 372 + 373 + static int bt1_apb_probe(struct platform_device *pdev) 374 + { 375 + struct bt1_apb *apb; 376 + int ret; 377 + 378 + apb = bt1_apb_create_data(pdev); 379 + if (IS_ERR(apb)) 380 + return PTR_ERR(apb); 381 + 382 + ret = bt1_apb_request_regs(apb); 383 + if (ret) 384 + return ret; 385 + 386 + ret = bt1_apb_request_rst(apb); 387 + if (ret) 388 + return ret; 389 + 390 + ret = bt1_apb_request_clk(apb); 391 + if (ret) 392 + return ret; 393 + 394 + ret = bt1_apb_request_irq(apb); 395 + if (ret) 396 + return ret; 397 + 398 + ret = bt1_apb_init_sysfs(apb); 399 + if (ret) 400 + return ret; 401 + 402 + return 0; 403 + } 404 + 405 + static const struct of_device_id bt1_apb_of_match[] = { 406 + { .compatible = "baikal,bt1-apb" }, 407 + { } 408 + }; 409 + MODULE_DEVICE_TABLE(of, bt1_apb_of_match); 410 + 411 + static struct platform_driver bt1_apb_driver = { 412 + .probe = bt1_apb_probe, 413 + .driver = { 414 + .name = "bt1-apb", 415 + .of_match_table = bt1_apb_of_match 416 + } 417 + }; 418 + module_platform_driver(bt1_apb_driver); 419 + 420 + MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 421 + MODULE_DESCRIPTION("Baikal-T1 APB-bus driver"); 422 + MODULE_LICENSE("GPL v2");
+320
drivers/bus/bt1-axi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4 + * 5 + * Authors: 6 + * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7 + * 8 + * Baikal-T1 AXI-bus driver 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/types.h> 14 + #include <linux/bitfield.h> 15 + #include <linux/device.h> 16 + #include <linux/atomic.h> 17 + #include <linux/regmap.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/mfd/syscon.h> 20 + #include <linux/interrupt.h> 21 + #include <linux/io.h> 22 + #include <linux/nmi.h> 23 + #include <linux/of.h> 24 + #include <linux/clk.h> 25 + #include <linux/reset.h> 26 + #include <linux/sysfs.h> 27 + 28 + #define BT1_AXI_WERRL 0x110 29 + #define BT1_AXI_WERRH 0x114 30 + #define BT1_AXI_WERRH_TYPE BIT(23) 31 + #define BT1_AXI_WERRH_ADDR_FLD 24 32 + #define BT1_AXI_WERRH_ADDR_MASK GENMASK(31, BT1_AXI_WERRH_ADDR_FLD) 33 + 34 + /* 35 + * struct bt1_axi - Baikal-T1 AXI-bus private data 36 + * @dev: Pointer to the device structure. 37 + * @qos_regs: AXI Interconnect QoS tuning registers. 38 + * @sys_regs: Baikal-T1 System Controller registers map. 39 + * @irq: Errors IRQ number. 40 + * @aclk: AXI reference clock. 41 + * @arst: AXI Interconnect reset line. 42 + * @count: Number of errors detected. 43 + */ 44 + struct bt1_axi { 45 + struct device *dev; 46 + 47 + void __iomem *qos_regs; 48 + struct regmap *sys_regs; 49 + int irq; 50 + 51 + struct clk *aclk; 52 + 53 + struct reset_control *arst; 54 + 55 + atomic_t count; 56 + }; 57 + 58 + static irqreturn_t bt1_axi_isr(int irq, void *data) 59 + { 60 + struct bt1_axi *axi = data; 61 + u32 low = 0, high = 0; 62 + 63 + regmap_read(axi->sys_regs, BT1_AXI_WERRL, &low); 64 + regmap_read(axi->sys_regs, BT1_AXI_WERRH, &high); 65 + 66 + dev_crit_ratelimited(axi->dev, 67 + "AXI-bus fault %d: %s at 0x%x%08x\n", 68 + atomic_inc_return(&axi->count), 69 + high & BT1_AXI_WERRH_TYPE ? "no slave" : "slave protocol error", 70 + high, low); 71 + 72 + /* 73 + * Print backtrace on each CPU. This might be pointless if the fault 74 + * has happened on the same CPU as the IRQ handler is executed or 75 + * the other core proceeded further execution despite the error. 76 + * But if it's not, by looking at the trace we would get straight to 77 + * the cause of the problem. 78 + */ 79 + trigger_all_cpu_backtrace(); 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static void bt1_axi_clear_data(void *data) 85 + { 86 + struct bt1_axi *axi = data; 87 + struct platform_device *pdev = to_platform_device(axi->dev); 88 + 89 + platform_set_drvdata(pdev, NULL); 90 + } 91 + 92 + static struct bt1_axi *bt1_axi_create_data(struct platform_device *pdev) 93 + { 94 + struct device *dev = &pdev->dev; 95 + struct bt1_axi *axi; 96 + int ret; 97 + 98 + axi = devm_kzalloc(dev, sizeof(*axi), GFP_KERNEL); 99 + if (!axi) 100 + return ERR_PTR(-ENOMEM); 101 + 102 + ret = devm_add_action(dev, bt1_axi_clear_data, axi); 103 + if (ret) { 104 + dev_err(dev, "Can't add AXI EHB data clear action\n"); 105 + return ERR_PTR(ret); 106 + } 107 + 108 + axi->dev = dev; 109 + atomic_set(&axi->count, 0); 110 + platform_set_drvdata(pdev, axi); 111 + 112 + return axi; 113 + } 114 + 115 + static int bt1_axi_request_regs(struct bt1_axi *axi) 116 + { 117 + struct platform_device *pdev = to_platform_device(axi->dev); 118 + struct device *dev = axi->dev; 119 + 120 + axi->sys_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); 121 + if (IS_ERR(axi->sys_regs)) { 122 + dev_err(dev, "Couldn't find syscon registers\n"); 123 + return PTR_ERR(axi->sys_regs); 124 + } 125 + 126 + axi->qos_regs = devm_platform_ioremap_resource_byname(pdev, "qos"); 127 + if (IS_ERR(axi->qos_regs)) { 128 + dev_err(dev, "Couldn't map AXI-bus QoS registers\n"); 129 + return PTR_ERR(axi->qos_regs); 130 + } 131 + 132 + return 0; 133 + } 134 + 135 + static int bt1_axi_request_rst(struct bt1_axi *axi) 136 + { 137 + int ret; 138 + 139 + axi->arst = devm_reset_control_get_optional_exclusive(axi->dev, "arst"); 140 + if (IS_ERR(axi->arst)) { 141 + dev_warn(axi->dev, "Couldn't get reset control line\n"); 142 + return PTR_ERR(axi->arst); 143 + } 144 + 145 + ret = reset_control_deassert(axi->arst); 146 + if (ret) 147 + dev_err(axi->dev, "Failed to deassert the reset line\n"); 148 + 149 + return ret; 150 + } 151 + 152 + static void bt1_axi_disable_clk(void *data) 153 + { 154 + struct bt1_axi *axi = data; 155 + 156 + clk_disable_unprepare(axi->aclk); 157 + } 158 + 159 + static int bt1_axi_request_clk(struct bt1_axi *axi) 160 + { 161 + int ret; 162 + 163 + axi->aclk = devm_clk_get(axi->dev, "aclk"); 164 + if (IS_ERR(axi->aclk)) { 165 + dev_err(axi->dev, "Couldn't get AXI Interconnect clock\n"); 166 + return PTR_ERR(axi->aclk); 167 + } 168 + 169 + ret = clk_prepare_enable(axi->aclk); 170 + if (ret) { 171 + dev_err(axi->dev, "Couldn't enable the AXI clock\n"); 172 + return ret; 173 + } 174 + 175 + ret = devm_add_action_or_reset(axi->dev, bt1_axi_disable_clk, axi); 176 + if (ret) { 177 + dev_err(axi->dev, "Can't add AXI clock disable action\n"); 178 + return ret; 179 + } 180 + 181 + return 0; 182 + } 183 + 184 + static int bt1_axi_request_irq(struct bt1_axi *axi) 185 + { 186 + struct platform_device *pdev = to_platform_device(axi->dev); 187 + int ret; 188 + 189 + axi->irq = platform_get_irq(pdev, 0); 190 + if (axi->irq < 0) 191 + return axi->irq; 192 + 193 + ret = devm_request_irq(axi->dev, axi->irq, bt1_axi_isr, IRQF_SHARED, 194 + "bt1-axi", axi); 195 + if (ret) { 196 + dev_err(axi->dev, "Couldn't request AXI EHB IRQ\n"); 197 + return ret; 198 + } 199 + 200 + return 0; 201 + } 202 + 203 + static ssize_t count_show(struct device *dev, 204 + struct device_attribute *attr, char *buf) 205 + { 206 + struct bt1_axi *axi = dev_get_drvdata(dev); 207 + 208 + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&axi->count)); 209 + } 210 + static DEVICE_ATTR_RO(count); 211 + 212 + static ssize_t inject_error_show(struct device *dev, 213 + struct device_attribute *attr, char *buf) 214 + { 215 + return scnprintf(buf, PAGE_SIZE, "Error injection: bus unaligned\n"); 216 + } 217 + 218 + static ssize_t inject_error_store(struct device *dev, 219 + struct device_attribute *attr, 220 + const char *data, size_t count) 221 + { 222 + struct bt1_axi *axi = dev_get_drvdata(dev); 223 + 224 + /* 225 + * Performing unaligned read from the memory will cause the CM2 bus 226 + * error while unaligned writing - the AXI bus write error handled 227 + * by this driver. 228 + */ 229 + if (!strncmp(data, "bus", 3)) 230 + readb(axi->qos_regs); 231 + else if (!strncmp(data, "unaligned", 9)) 232 + writeb(0, axi->qos_regs); 233 + else 234 + return -EINVAL; 235 + 236 + return count; 237 + } 238 + static DEVICE_ATTR_RW(inject_error); 239 + 240 + static struct attribute *bt1_axi_sysfs_attrs[] = { 241 + &dev_attr_count.attr, 242 + &dev_attr_inject_error.attr, 243 + NULL 244 + }; 245 + ATTRIBUTE_GROUPS(bt1_axi_sysfs); 246 + 247 + static void bt1_axi_remove_sysfs(void *data) 248 + { 249 + struct bt1_axi *axi = data; 250 + 251 + device_remove_groups(axi->dev, bt1_axi_sysfs_groups); 252 + } 253 + 254 + static int bt1_axi_init_sysfs(struct bt1_axi *axi) 255 + { 256 + int ret; 257 + 258 + ret = device_add_groups(axi->dev, bt1_axi_sysfs_groups); 259 + if (ret) { 260 + dev_err(axi->dev, "Failed to add sysfs files group\n"); 261 + return ret; 262 + } 263 + 264 + ret = devm_add_action_or_reset(axi->dev, bt1_axi_remove_sysfs, axi); 265 + if (ret) 266 + dev_err(axi->dev, "Can't add AXI EHB sysfs remove action\n"); 267 + 268 + return ret; 269 + } 270 + 271 + static int bt1_axi_probe(struct platform_device *pdev) 272 + { 273 + struct bt1_axi *axi; 274 + int ret; 275 + 276 + axi = bt1_axi_create_data(pdev); 277 + if (IS_ERR(axi)) 278 + return PTR_ERR(axi); 279 + 280 + ret = bt1_axi_request_regs(axi); 281 + if (ret) 282 + return ret; 283 + 284 + ret = bt1_axi_request_rst(axi); 285 + if (ret) 286 + return ret; 287 + 288 + ret = bt1_axi_request_clk(axi); 289 + if (ret) 290 + return ret; 291 + 292 + ret = bt1_axi_request_irq(axi); 293 + if (ret) 294 + return ret; 295 + 296 + ret = bt1_axi_init_sysfs(axi); 297 + if (ret) 298 + return ret; 299 + 300 + return 0; 301 + } 302 + 303 + static const struct of_device_id bt1_axi_of_match[] = { 304 + { .compatible = "baikal,bt1-axi" }, 305 + { } 306 + }; 307 + MODULE_DEVICE_TABLE(of, bt1_axi_of_match); 308 + 309 + static struct platform_driver bt1_axi_driver = { 310 + .probe = bt1_axi_probe, 311 + .driver = { 312 + .name = "bt1-axi", 313 + .of_match_table = bt1_axi_of_match 314 + } 315 + }; 316 + module_platform_driver(bt1_axi_driver); 317 + 318 + MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 319 + MODULE_DESCRIPTION("Baikal-T1 AXI-bus driver"); 320 + MODULE_LICENSE("GPL v2");
+11
drivers/memory/Kconfig
··· 46 46 tree is used. This bus supports NANDs, external ethernet controller, 47 47 SRAMs, ATA devices, etc. 48 48 49 + config BT1_L2_CTL 50 + bool "Baikal-T1 CM2 L2-RAM Cache Control Block" 51 + depends on MIPS_BAIKAL_T1 || COMPILE_TEST 52 + select MFD_SYSCON 53 + help 54 + Baikal-T1 CPU is based on the MIPS P5600 Warrior IP-core. The CPU 55 + resides Coherency Manager v2 with embedded 1MB L2-cache. It's 56 + possible to tune the L2 cache performance up by setting the data, 57 + tags and way-select latencies of RAM access. This driver provides a 58 + dt properties-based and sysfs interface for it. 59 + 49 60 config TI_AEMIF 50 61 tristate "Texas Instruments AEMIF driver" 51 62 depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
+1
drivers/memory/Makefile
··· 11 11 obj-$(CONFIG_ATMEL_SDRAMC) += atmel-sdramc.o 12 12 obj-$(CONFIG_ATMEL_EBI) += atmel-ebi.o 13 13 obj-$(CONFIG_ARCH_BRCMSTB) += brcmstb_dpfe.o 14 + obj-$(CONFIG_BT1_L2_CTL) += bt1-l2-ctl.o 14 15 obj-$(CONFIG_TI_AEMIF) += ti-aemif.o 15 16 obj-$(CONFIG_TI_EMIF) += emif.o 16 17 obj-$(CONFIG_OMAP_GPMC) += omap-gpmc.o
+322
drivers/memory/bt1-l2-ctl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4 + * 5 + * Authors: 6 + * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7 + * 8 + * Baikal-T1 CM2 L2-cache Control Block driver. 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/bitfield.h> 14 + #include <linux/types.h> 15 + #include <linux/device.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/regmap.h> 18 + #include <linux/mfd/syscon.h> 19 + #include <linux/sysfs.h> 20 + #include <linux/of.h> 21 + 22 + #define L2_CTL_REG 0x028 23 + #define L2_CTL_DATA_STALL_FLD 0 24 + #define L2_CTL_DATA_STALL_MASK GENMASK(1, L2_CTL_DATA_STALL_FLD) 25 + #define L2_CTL_TAG_STALL_FLD 2 26 + #define L2_CTL_TAG_STALL_MASK GENMASK(3, L2_CTL_TAG_STALL_FLD) 27 + #define L2_CTL_WS_STALL_FLD 4 28 + #define L2_CTL_WS_STALL_MASK GENMASK(5, L2_CTL_WS_STALL_FLD) 29 + #define L2_CTL_SET_CLKRATIO BIT(13) 30 + #define L2_CTL_CLKRATIO_LOCK BIT(31) 31 + 32 + #define L2_CTL_STALL_MIN 0 33 + #define L2_CTL_STALL_MAX 3 34 + #define L2_CTL_STALL_SET_DELAY_US 1 35 + #define L2_CTL_STALL_SET_TOUT_US 1000 36 + 37 + /* 38 + * struct l2_ctl - Baikal-T1 L2 Control block private data. 39 + * @dev: Pointer to the device structure. 40 + * @sys_regs: Baikal-T1 System Controller registers map. 41 + */ 42 + struct l2_ctl { 43 + struct device *dev; 44 + 45 + struct regmap *sys_regs; 46 + }; 47 + 48 + /* 49 + * enum l2_ctl_stall - Baikal-T1 L2-cache-RAM stall identifier. 50 + * @L2_WSSTALL: Way-select latency. 51 + * @L2_TAGSTALL: Tag latency. 52 + * @L2_DATASTALL: Data latency. 53 + */ 54 + enum l2_ctl_stall { 55 + L2_WS_STALL, 56 + L2_TAG_STALL, 57 + L2_DATA_STALL 58 + }; 59 + 60 + /* 61 + * struct l2_ctl_device_attribute - Baikal-T1 L2-cache device attribute. 62 + * @dev_attr: Actual sysfs device attribute. 63 + * @id: L2-cache stall field identifier. 64 + */ 65 + struct l2_ctl_device_attribute { 66 + struct device_attribute dev_attr; 67 + enum l2_ctl_stall id; 68 + }; 69 + #define to_l2_ctl_dev_attr(_dev_attr) \ 70 + container_of(_dev_attr, struct l2_ctl_device_attribute, dev_attr) 71 + 72 + #define L2_CTL_ATTR_RW(_name, _prefix, _id) \ 73 + struct l2_ctl_device_attribute l2_ctl_attr_##_name = \ 74 + { __ATTR(_name, 0644, _prefix##_show, _prefix##_store), _id } 75 + 76 + static int l2_ctl_get_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 *val) 77 + { 78 + u32 data = 0; 79 + int ret; 80 + 81 + ret = regmap_read(l2->sys_regs, L2_CTL_REG, &data); 82 + if (ret) 83 + return ret; 84 + 85 + switch (id) { 86 + case L2_WS_STALL: 87 + *val = FIELD_GET(L2_CTL_WS_STALL_MASK, data); 88 + break; 89 + case L2_TAG_STALL: 90 + *val = FIELD_GET(L2_CTL_TAG_STALL_MASK, data); 91 + break; 92 + case L2_DATA_STALL: 93 + *val = FIELD_GET(L2_CTL_DATA_STALL_MASK, data); 94 + break; 95 + default: 96 + return -EINVAL; 97 + } 98 + 99 + return 0; 100 + } 101 + 102 + static int l2_ctl_set_latency(struct l2_ctl *l2, enum l2_ctl_stall id, u32 val) 103 + { 104 + u32 mask = 0, data = 0; 105 + int ret; 106 + 107 + val = clamp_val(val, L2_CTL_STALL_MIN, L2_CTL_STALL_MAX); 108 + 109 + switch (id) { 110 + case L2_WS_STALL: 111 + data = FIELD_PREP(L2_CTL_WS_STALL_MASK, val); 112 + mask = L2_CTL_WS_STALL_MASK; 113 + break; 114 + case L2_TAG_STALL: 115 + data = FIELD_PREP(L2_CTL_TAG_STALL_MASK, val); 116 + mask = L2_CTL_TAG_STALL_MASK; 117 + break; 118 + case L2_DATA_STALL: 119 + data = FIELD_PREP(L2_CTL_DATA_STALL_MASK, val); 120 + mask = L2_CTL_DATA_STALL_MASK; 121 + break; 122 + default: 123 + return -EINVAL; 124 + } 125 + 126 + data |= L2_CTL_SET_CLKRATIO; 127 + mask |= L2_CTL_SET_CLKRATIO; 128 + 129 + ret = regmap_update_bits(l2->sys_regs, L2_CTL_REG, mask, data); 130 + if (ret) 131 + return ret; 132 + 133 + return regmap_read_poll_timeout(l2->sys_regs, L2_CTL_REG, data, 134 + data & L2_CTL_CLKRATIO_LOCK, 135 + L2_CTL_STALL_SET_DELAY_US, 136 + L2_CTL_STALL_SET_TOUT_US); 137 + } 138 + 139 + static void l2_ctl_clear_data(void *data) 140 + { 141 + struct l2_ctl *l2 = data; 142 + struct platform_device *pdev = to_platform_device(l2->dev); 143 + 144 + platform_set_drvdata(pdev, NULL); 145 + } 146 + 147 + static struct l2_ctl *l2_ctl_create_data(struct platform_device *pdev) 148 + { 149 + struct device *dev = &pdev->dev; 150 + struct l2_ctl *l2; 151 + int ret; 152 + 153 + l2 = devm_kzalloc(dev, sizeof(*l2), GFP_KERNEL); 154 + if (!l2) 155 + return ERR_PTR(-ENOMEM); 156 + 157 + ret = devm_add_action(dev, l2_ctl_clear_data, l2); 158 + if (ret) { 159 + dev_err(dev, "Can't add L2 CTL data clear action\n"); 160 + return ERR_PTR(ret); 161 + } 162 + 163 + l2->dev = dev; 164 + platform_set_drvdata(pdev, l2); 165 + 166 + return l2; 167 + } 168 + 169 + static int l2_ctl_find_sys_regs(struct l2_ctl *l2) 170 + { 171 + l2->sys_regs = syscon_node_to_regmap(l2->dev->of_node->parent); 172 + if (IS_ERR(l2->sys_regs)) { 173 + dev_err(l2->dev, "Couldn't get L2 CTL register map\n"); 174 + return PTR_ERR(l2->sys_regs); 175 + } 176 + 177 + return 0; 178 + } 179 + 180 + static int l2_ctl_of_parse_property(struct l2_ctl *l2, enum l2_ctl_stall id, 181 + const char *propname) 182 + { 183 + int ret = 0; 184 + u32 data; 185 + 186 + if (!of_property_read_u32(l2->dev->of_node, propname, &data)) { 187 + ret = l2_ctl_set_latency(l2, id, data); 188 + if (ret) 189 + dev_err(l2->dev, "Invalid value of '%s'\n", propname); 190 + } 191 + 192 + return ret; 193 + } 194 + 195 + static int l2_ctl_of_parse(struct l2_ctl *l2) 196 + { 197 + int ret; 198 + 199 + ret = l2_ctl_of_parse_property(l2, L2_WS_STALL, "baikal,l2-ws-latency"); 200 + if (ret) 201 + return ret; 202 + 203 + ret = l2_ctl_of_parse_property(l2, L2_TAG_STALL, "baikal,l2-tag-latency"); 204 + if (ret) 205 + return ret; 206 + 207 + return l2_ctl_of_parse_property(l2, L2_DATA_STALL, 208 + "baikal,l2-data-latency"); 209 + } 210 + 211 + static ssize_t l2_ctl_latency_show(struct device *dev, 212 + struct device_attribute *attr, 213 + char *buf) 214 + { 215 + struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr); 216 + struct l2_ctl *l2 = dev_get_drvdata(dev); 217 + u32 data; 218 + int ret; 219 + 220 + ret = l2_ctl_get_latency(l2, devattr->id, &data); 221 + if (ret) 222 + return ret; 223 + 224 + return scnprintf(buf, PAGE_SIZE, "%u\n", data); 225 + } 226 + 227 + static ssize_t l2_ctl_latency_store(struct device *dev, 228 + struct device_attribute *attr, 229 + const char *buf, size_t count) 230 + { 231 + struct l2_ctl_device_attribute *devattr = to_l2_ctl_dev_attr(attr); 232 + struct l2_ctl *l2 = dev_get_drvdata(dev); 233 + u32 data; 234 + int ret; 235 + 236 + if (kstrtouint(buf, 0, &data) < 0) 237 + return -EINVAL; 238 + 239 + ret = l2_ctl_set_latency(l2, devattr->id, data); 240 + if (ret) 241 + return ret; 242 + 243 + return count; 244 + } 245 + static L2_CTL_ATTR_RW(l2_ws_latency, l2_ctl_latency, L2_WS_STALL); 246 + static L2_CTL_ATTR_RW(l2_tag_latency, l2_ctl_latency, L2_TAG_STALL); 247 + static L2_CTL_ATTR_RW(l2_data_latency, l2_ctl_latency, L2_DATA_STALL); 248 + 249 + static struct attribute *l2_ctl_sysfs_attrs[] = { 250 + &l2_ctl_attr_l2_ws_latency.dev_attr.attr, 251 + &l2_ctl_attr_l2_tag_latency.dev_attr.attr, 252 + &l2_ctl_attr_l2_data_latency.dev_attr.attr, 253 + NULL 254 + }; 255 + ATTRIBUTE_GROUPS(l2_ctl_sysfs); 256 + 257 + static void l2_ctl_remove_sysfs(void *data) 258 + { 259 + struct l2_ctl *l2 = data; 260 + 261 + device_remove_groups(l2->dev, l2_ctl_sysfs_groups); 262 + } 263 + 264 + static int l2_ctl_init_sysfs(struct l2_ctl *l2) 265 + { 266 + int ret; 267 + 268 + ret = device_add_groups(l2->dev, l2_ctl_sysfs_groups); 269 + if (ret) { 270 + dev_err(l2->dev, "Failed to create L2 CTL sysfs nodes\n"); 271 + return ret; 272 + } 273 + 274 + ret = devm_add_action_or_reset(l2->dev, l2_ctl_remove_sysfs, l2); 275 + if (ret) 276 + dev_err(l2->dev, "Can't add L2 CTL sysfs remove action\n"); 277 + 278 + return ret; 279 + } 280 + 281 + static int l2_ctl_probe(struct platform_device *pdev) 282 + { 283 + struct l2_ctl *l2; 284 + int ret; 285 + 286 + l2 = l2_ctl_create_data(pdev); 287 + if (IS_ERR(l2)) 288 + return PTR_ERR(l2); 289 + 290 + ret = l2_ctl_find_sys_regs(l2); 291 + if (ret) 292 + return ret; 293 + 294 + ret = l2_ctl_of_parse(l2); 295 + if (ret) 296 + return ret; 297 + 298 + ret = l2_ctl_init_sysfs(l2); 299 + if (ret) 300 + return ret; 301 + 302 + return 0; 303 + } 304 + 305 + static const struct of_device_id l2_ctl_of_match[] = { 306 + { .compatible = "baikal,bt1-l2-ctl" }, 307 + { } 308 + }; 309 + MODULE_DEVICE_TABLE(of, l2_ctl_of_match); 310 + 311 + static struct platform_driver l2_ctl_driver = { 312 + .probe = l2_ctl_probe, 313 + .driver = { 314 + .name = "bt1-l2-ctl", 315 + .of_match_table = l2_ctl_of_match 316 + } 317 + }; 318 + module_platform_driver(l2_ctl_driver); 319 + 320 + MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 321 + MODULE_DESCRIPTION("Baikal-T1 L2-cache driver"); 322 + MODULE_LICENSE("GPL v2");