From 3fbcc6763bb2b6410b2051c983275cd99ba1b5f2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 17 Jul 2021 02:05:25 +0200 Subject: [PATCH 1/8] bus: ixp4xx: Add DT bindings for the IXP4xx expansion bus This adds device tree bindings for the IXP4xx expansion bus controller. Cc: Marc Zyngier Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- ...intel,ixp4xx-expansion-bus-controller.yaml | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml diff --git a/Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml b/Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml new file mode 100644 index 000000000000..5fb4e7bfa4da --- /dev/null +++ b/Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/bus/intel,ixp4xx-expansion-bus-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel IXP4xx Expansion Bus Controller + +description: | + The IXP4xx expansion bus controller handles access to devices on the + memory-mapped expansion bus on the Intel IXP4xx family of system on chips, + including IXP42x, IXP43x, IXP45x and IXP46x. + +maintainers: + - Linus Walleij + +properties: + $nodename: + pattern: '^bus@[0-9a-f]+$' + + compatible: + items: + - enum: + - intel,ixp42x-expansion-bus-controller + - intel,ixp43x-expansion-bus-controller + - intel,ixp45x-expansion-bus-controller + - intel,ixp46x-expansion-bus-controller + - const: syscon + + reg: + description: Control registers for the expansion bus, these are not + inside the memory range handled by the expansion bus. + maxItems: 1 + + native-endian: + $ref: /schemas/types.yaml#/definitions/flag + description: The IXP4xx has a peculiar MMIO access scheme, as it changes + the access pattern for words (swizzling) on the bus depending on whether + the SoC is running in big-endian or little-endian mode. Thus the + registers must always be accessed using native endianness. + + "#address-cells": + description: | + The first cell is the chip select number. + The second cell is the address offset within the bank. + const: 2 + + "#size-cells": + const: 1 + + ranges: true + dma-ranges: true + +patternProperties: + "^.*@[0-7],[0-9a-f]+$": + description: Devices attached to chip selects are represented as + subnodes. + type: object + + properties: + intel,ixp4xx-eb-t1: + description: Address timing, extend address phase with n cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 3 + + intel,ixp4xx-eb-t2: + description: Setup chip select timing, extend setup phase with n cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 3 + + intel,ixp4xx-eb-t3: + description: Strobe timing, extend strobe phase with n cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 15 + + intel,ixp4xx-eb-t4: + description: Hold timing, extend hold phase with n cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 3 + + intel,ixp4xx-eb-t5: + description: Recovery timing, extend recovery phase with n cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + maximum: 15 + + intel,ixp4xx-eb-cycle-type: + description: The type of cycles to use on the expansion bus for this + chip select. 0 = Intel cycles, 1 = Motorola cycles, 2 = HPI cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + + intel,ixp4xx-eb-byte-access-on-halfword: + description: Allow byte read access on half word devices. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + intel,ixp4xx-eb-hpi-hrdy-pol-high: + description: Set HPI HRDY polarity to active high when using HPI. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + intel,ixp4xx-eb-mux-address-and-data: + description: Multiplex address and data on the data bus. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + intel,ixp4xx-eb-ahb-split-transfers: + description: Enable AHB split transfers. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + intel,ixp4xx-eb-write-enable: + description: Enable write cycles. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + + intel,ixp4xx-eb-byte-access: + description: Expansion bus uses only 8 bits. The default is to use + 16 bits. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + +required: + - compatible + - reg + - native-endian + - "#address-cells" + - "#size-cells" + - ranges + - dma-ranges + +additionalProperties: false + +examples: + - | + #include + bus@50000000 { + compatible = "intel,ixp42x-expansion-bus-controller", "syscon"; + reg = <0xc4000000 0x28>; + native-endian; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0x0 0x50000000 0x01000000>, + <1 0x0 0x51000000 0x01000000>; + dma-ranges = <0 0x0 0x50000000 0x01000000>, + <1 0x0 0x51000000 0x01000000>; + flash@0,0 { + compatible = "intel,ixp4xx-flash", "cfi-flash"; + bank-width = <2>; + reg = <0 0x00000000 0x1000000>; + intel,ixp4xx-eb-t3 = <3>; + intel,ixp4xx-eb-cycle-type = <0>; + intel,ixp4xx-eb-byte-access-on-halfword = <1>; + intel,ixp4xx-eb-write-enable = <1>; + intel,ixp4xx-eb-byte-access = <0>; + }; + serial@1,0 { + compatible = "exar,xr16l2551", "ns8250"; + reg = <1 0x00000000 0x10>; + interrupt-parent = <&gpio0>; + interrupts = <4 IRQ_TYPE_LEVEL_LOW>; + clock-frequency = <1843200>; + intel,ixp4xx-eb-t3 = <3>; + intel,ixp4xx-eb-cycle-type = <1>; + intel,ixp4xx-eb-write-enable = <1>; + intel,ixp4xx-eb-byte-access = <1>; + }; + }; From 1c953bda90ca7e4a2574a738e41a04a2bbc03bd2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 16 Jul 2021 01:53:34 +0200 Subject: [PATCH 2/8] bus: ixp4xx: Add a driver for IXP4xx expansion bus The Intel IXP4xx SoCs have an expansion bus that is usually just used for flash memory and configured by the boot loaders and can be accessed using the "simple-bus". However some devices need more elaborate configuration and then we need to provide a proper 3-unit address space indicating chip select for each device and provide timing and similar information. Cc: Marc Zyngier Signed-off-by: Linus Walleij --- MAINTAINERS | 2 + drivers/bus/Kconfig | 11 + drivers/bus/Makefile | 1 + drivers/bus/intel-ixp4xx-eb.c | 429 ++++++++++++++++++++++++++++++++++ 4 files changed, 443 insertions(+) create mode 100644 drivers/bus/intel-ixp4xx-eb.c diff --git a/MAINTAINERS b/MAINTAINERS index a61f4f3b78a9..883da28d8406 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2009,10 +2009,12 @@ M: Krzysztof Halasa L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml +F: Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml F: Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt F: Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml F: Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml F: arch/arm/mach-ixp4xx/ +F: drivers/bus/intel-ixp4xx-eb.c F: drivers/clocksource/timer-ixp4xx.c F: drivers/crypto/ixp4xx_crypto.c F: drivers/gpio/gpio-ixp4xx.c diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index e7f7eee6ee9a..a5b96f3aad67 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -95,6 +95,17 @@ config IMX_WEIM The WEIM(Wireless External Interface Module) works like a bus. You can attach many different devices on it, such as NOR, onenand. +config INTEL_IXP4XX_EB + bool "Intel IXP4xx expansion bus interface driver" + depends on HAS_IOMEM + depends on ARCH_IXP4XX || COMPILE_TEST + default ARCH_IXP4XX + select MFD_SYSCON + help + Driver for the Intel IXP4xx expansion bus interface. The driver is + needed to set up various chip select configuration parameters before + devices on the expansion bus can be discovered. + config MIPS_CDMM bool "MIPS Common Device Memory Map (CDMM) Driver" depends on CPU_MIPSR2 || CPU_MIPSR5 diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 397e35392bff..1c29c5e8ffb8 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ obj-$(CONFIG_BT1_APB) += bt1-apb.o obj-$(CONFIG_BT1_AXI) += bt1-axi.o obj-$(CONFIG_IMX_WEIM) += imx-weim.o +obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o diff --git a/drivers/bus/intel-ixp4xx-eb.c b/drivers/bus/intel-ixp4xx-eb.c new file mode 100644 index 000000000000..9acd00da413c --- /dev/null +++ b/drivers/bus/intel-ixp4xx-eb.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel IXP4xx Expansion Bus Controller + * Copyright (C) 2021 Linaro Ltd. + * + * Author: Linus Walleij + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IXP4XX_EXP_NUM_CS 8 + +#define IXP4XX_EXP_TIMING_CS0 0x00 +#define IXP4XX_EXP_TIMING_CS1 0x04 +#define IXP4XX_EXP_TIMING_CS2 0x08 +#define IXP4XX_EXP_TIMING_CS3 0x0c +#define IXP4XX_EXP_TIMING_CS4 0x10 +#define IXP4XX_EXP_TIMING_CS5 0x14 +#define IXP4XX_EXP_TIMING_CS6 0x18 +#define IXP4XX_EXP_TIMING_CS7 0x1c + +/* Bits inside each CS timing register */ +#define IXP4XX_EXP_TIMING_STRIDE 0x04 +#define IXP4XX_EXP_CS_EN BIT(31) +#define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */ +#define IXP4XX_EXP_T1_MASK GENMASK(28, 27) +#define IXP4XX_EXP_T1_SHIFT 28 +#define IXP4XX_EXP_T2_MASK GENMASK(27, 26) +#define IXP4XX_EXP_T2_SHIFT 26 +#define IXP4XX_EXP_T3_MASK GENMASK(25, 22) +#define IXP4XX_EXP_T3_SHIFT 22 +#define IXP4XX_EXP_T4_MASK GENMASK(21, 20) +#define IXP4XX_EXP_T4_SHIFT 20 +#define IXP4XX_EXP_T5_MASK GENMASK(19, 16) +#define IXP4XX_EXP_T5_SHIFT 16 +#define IXP4XX_EXP_CYC_TYPE_MASK GENMASK(15, 14) +#define IXP4XX_EXP_CYC_TYPE_SHIFT 14 +#define IXP4XX_EXP_SIZE_MASK GENMASK(13, 10) +#define IXP4XX_EXP_SIZE_SHIFT 10 +#define IXP4XX_EXP_CNFG_0 BIT(9) /* Always zero */ +#define IXP43X_EXP_SYNC_INTEL BIT(8) /* Only on IXP43x */ +#define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x */ +#define IXP4XX_EXP_BYTE_RD16 BIT(6) +#define IXP4XX_EXP_HRDY_POL BIT(5) /* Only on IXP42x */ +#define IXP4XX_EXP_MUX_EN BIT(4) +#define IXP4XX_EXP_SPLT_EN BIT(3) +#define IXP4XX_EXP_WORD BIT(2) /* Always zero */ +#define IXP4XX_EXP_WR_EN BIT(1) +#define IXP4XX_EXP_BYTE_EN BIT(0) +#define IXP42X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(8)|BIT(7)|IXP4XX_EXP_WORD) +#define IXP43X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(5)|IXP4XX_EXP_WORD) + +#define IXP4XX_EXP_CNFG0 0x20 +#define IXP4XX_EXP_CNFG0_MEM_MAP BIT(31) +#define IXP4XX_EXP_CNFG1 0x24 + +#define IXP4XX_EXP_BOOT_BASE 0x00000000 +#define IXP4XX_EXP_NORMAL_BASE 0x50000000 +#define IXP4XX_EXP_STRIDE 0x01000000 + +/* Fuses on the IXP43x */ +#define IXP43X_EXP_UNIT_FUSE_RESET 0x28 +#define IXP43x_EXP_FUSE_SPEED_MASK GENMASK(23, 22) + +/* Number of device tree values in "reg" */ +#define IXP4XX_OF_REG_SIZE 3 + +struct ixp4xx_eb { + struct device *dev; + struct regmap *rmap; + u32 bus_base; + bool is_42x; + bool is_43x; +}; + +struct ixp4xx_exp_tim_prop { + const char *prop; + u32 max; + u32 mask; + u16 shift; +}; + +static const struct ixp4xx_exp_tim_prop ixp4xx_exp_tim_props[] = { + { + .prop = "intel,ixp4xx-eb-t1", + .max = 3, + .mask = IXP4XX_EXP_T1_MASK, + .shift = IXP4XX_EXP_T1_SHIFT, + }, + { + .prop = "intel,ixp4xx-eb-t2", + .max = 3, + .mask = IXP4XX_EXP_T2_MASK, + .shift = IXP4XX_EXP_T2_SHIFT, + }, + { + .prop = "intel,ixp4xx-eb-t3", + .max = 15, + .mask = IXP4XX_EXP_T3_MASK, + .shift = IXP4XX_EXP_T3_SHIFT, + }, + { + .prop = "intel,ixp4xx-eb-t4", + .max = 3, + .mask = IXP4XX_EXP_T4_MASK, + .shift = IXP4XX_EXP_T4_SHIFT, + }, + { + .prop = "intel,ixp4xx-eb-t5", + .max = 15, + .mask = IXP4XX_EXP_T5_MASK, + .shift = IXP4XX_EXP_T5_SHIFT, + }, + { + .prop = "intel,ixp4xx-eb-byte-access-on-halfword", + .max = 1, + .mask = IXP4XX_EXP_BYTE_RD16, + }, + { + .prop = "intel,ixp4xx-eb-hpi-hrdy-pol-high", + .max = 1, + .mask = IXP4XX_EXP_HRDY_POL, + }, + { + .prop = "intel,ixp4xx-eb-mux-address-and-data", + .max = 1, + .mask = IXP4XX_EXP_MUX_EN, + }, + { + .prop = "intel,ixp4xx-eb-ahb-split-transfers", + .max = 1, + .mask = IXP4XX_EXP_SPLT_EN, + }, + { + .prop = "intel,ixp4xx-eb-write-enable", + .max = 1, + .mask = IXP4XX_EXP_WR_EN, + }, + { + .prop = "intel,ixp4xx-eb-byte-access", + .max = 1, + .mask = IXP4XX_EXP_BYTE_EN, + }, +}; + +static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb, + struct device_node *np, + u32 cs_index, + u32 cs_size) +{ + u32 cs_cfg; + u32 val; + u32 cur_cssize; + u32 cs_order; + int ret; + int i; + + if (eb->is_42x && (cs_index > 7)) { + dev_err(eb->dev, + "invalid chipselect %u, we only support 0-7\n", + cs_index); + return; + } + if (eb->is_43x && (cs_index > 3)) { + dev_err(eb->dev, + "invalid chipselect %u, we only support 0-3\n", + cs_index); + return; + } + + /* Several chip selects can be joined into one device */ + if (cs_size > IXP4XX_EXP_STRIDE) + cur_cssize = IXP4XX_EXP_STRIDE; + else + cur_cssize = cs_size; + + + /* + * The following will read/modify/write the configuration for one + * chipselect, attempting to leave the boot defaults in place unless + * something is explicitly defined. + */ + regmap_read(eb->rmap, IXP4XX_EXP_TIMING_CS0 + + IXP4XX_EXP_TIMING_STRIDE * cs_index, &cs_cfg); + dev_info(eb->dev, "CS%d at %#08x, size %#08x, config before: %#08x\n", + cs_index, eb->bus_base + IXP4XX_EXP_STRIDE * cs_index, + cur_cssize, cs_cfg); + + /* Size set-up first align to 2^9 .. 2^24 */ + cur_cssize = roundup_pow_of_two(cur_cssize); + if (cur_cssize < 512) + cur_cssize = 512; + cs_order = ilog2(cur_cssize); + if (cs_order < 9 || cs_order > 24) { + dev_err(eb->dev, "illegal size order %d\n", cs_order); + return; + } + dev_dbg(eb->dev, "CS%d size order: %d\n", cs_index, cs_order); + cs_cfg &= ~(IXP4XX_EXP_SIZE_MASK); + cs_cfg |= ((cs_order - 9) << IXP4XX_EXP_SIZE_SHIFT); + + for (i = 0; i < ARRAY_SIZE(ixp4xx_exp_tim_props); i++) { + const struct ixp4xx_exp_tim_prop *ip = &ixp4xx_exp_tim_props[i]; + + /* All are regular u32 values */ + ret = of_property_read_u32(np, ip->prop, &val); + if (ret) + continue; + + /* Handle bools (single bits) first */ + if (ip->max == 1) { + if (val) + cs_cfg |= ip->mask; + else + cs_cfg &= ~ip->mask; + dev_info(eb->dev, "CS%d %s %s\n", cs_index, + val ? "enabled" : "disabled", + ip->prop); + continue; + } + + if (val > ip->max) { + dev_err(eb->dev, + "CS%d too high value for %s: %u, capped at %u\n", + cs_index, ip->prop, val, ip->max); + val = ip->max; + } + /* This assumes max value fills all the assigned bits (and it does) */ + cs_cfg &= ~ip->mask; + cs_cfg |= (val << ip->shift); + dev_info(eb->dev, "CS%d set %s to %u\n", cs_index, ip->prop, val); + } + + ret = of_property_read_u32(np, "intel,ixp4xx-eb-cycle-type", &val); + if (!ret) { + if (val > 3) { + dev_err(eb->dev, "illegal cycle type %d\n", val); + return; + } + dev_info(eb->dev, "CS%d set cycle type %d\n", cs_index, val); + cs_cfg &= ~IXP4XX_EXP_CYC_TYPE_MASK; + cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT; + } + + if (eb->is_42x) + cs_cfg &= ~IXP42X_RESERVED; + if (eb->is_43x) { + cs_cfg &= ~IXP43X_RESERVED; + /* + * This bit for Intel strata flash is currently unused, but let's + * report it if we find one. + */ + if (cs_cfg & IXP43X_EXP_SYNC_INTEL) + dev_info(eb->dev, "claims to be Intel strata flash\n"); + } + cs_cfg |= IXP4XX_EXP_CS_EN; + + regmap_write(eb->rmap, + IXP4XX_EXP_TIMING_CS0 + IXP4XX_EXP_TIMING_STRIDE * cs_index, + cs_cfg); + dev_info(eb->dev, "CS%d wrote %#08x into CS config\n", cs_index, cs_cfg); + + /* + * If several chip selects are joined together into one big + * device area, we call ourselves recursively for each successive + * chip select. For a 32MB flash chip this results in two calls + * for example. + */ + if (cs_size > IXP4XX_EXP_STRIDE) + ixp4xx_exp_setup_chipselect(eb, np, + cs_index + 1, + cs_size - IXP4XX_EXP_STRIDE); +} + +static void ixp4xx_exp_setup_child(struct ixp4xx_eb *eb, + struct device_node *np) +{ + u32 cs_sizes[IXP4XX_EXP_NUM_CS]; + int num_regs; + u32 csindex; + u32 cssize; + int ret; + int i; + + num_regs = of_property_count_elems_of_size(np, "reg", IXP4XX_OF_REG_SIZE); + if (num_regs <= 0) + return; + dev_dbg(eb->dev, "child %s has %d register sets\n", + of_node_full_name(np), num_regs); + + for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) + cs_sizes[csindex] = 0; + + for (i = 0; i < num_regs; i++) { + u32 rbase, rsize; + + ret = of_property_read_u32_index(np, "reg", + i * IXP4XX_OF_REG_SIZE, &csindex); + if (ret) + break; + ret = of_property_read_u32_index(np, "reg", + i * IXP4XX_OF_REG_SIZE + 1, &rbase); + if (ret) + break; + ret = of_property_read_u32_index(np, "reg", + i * IXP4XX_OF_REG_SIZE + 2, &rsize); + if (ret) + break; + + if (csindex >= IXP4XX_EXP_NUM_CS) { + dev_err(eb->dev, "illegal CS %d\n", csindex); + continue; + } + /* + * The memory window always starts from CS base so we need to add + * the start and size to get to the size from the start of the CS + * base. For example if CS0 is at 0x50000000 and the reg is + * <0 0xe40000 0x40000> the size is e80000. + * + * Roof this if we have several regs setting the same CS. + */ + cssize = rbase + rsize; + dev_dbg(eb->dev, "CS%d size %#08x\n", csindex, cssize); + if (cs_sizes[csindex] < cssize) + cs_sizes[csindex] = cssize; + } + + for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) { + cssize = cs_sizes[csindex]; + if (!cssize) + continue; + /* Just this one, so set it up and return */ + ixp4xx_exp_setup_chipselect(eb, np, csindex, cssize); + } +} + +static int ixp4xx_exp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct ixp4xx_eb *eb; + struct device_node *child; + bool have_children = false; + u32 val; + int ret; + + eb = devm_kzalloc(dev, sizeof(*eb), GFP_KERNEL); + if (!eb) + return -ENOMEM; + + eb->dev = dev; + eb->is_42x = of_device_is_compatible(np, "intel,ixp42x-expansion-bus-controller"); + eb->is_43x = of_device_is_compatible(np, "intel,ixp43x-expansion-bus-controller"); + + eb->rmap = syscon_node_to_regmap(np); + if (IS_ERR(eb->rmap)) + return dev_err_probe(dev, PTR_ERR(eb->rmap), "no regmap\n"); + + /* We check that the regmap work only on first read */ + ret = regmap_read(eb->rmap, IXP4XX_EXP_CNFG0, &val); + if (ret) + dev_err_probe(dev, ret, "cannot read regmap\n"); + if (val & IXP4XX_EXP_CNFG0_MEM_MAP) + eb->bus_base = IXP4XX_EXP_BOOT_BASE; + else + eb->bus_base = IXP4XX_EXP_NORMAL_BASE; + dev_info(dev, "expansion bus at %08x\n", eb->bus_base); + + if (eb->is_43x) { + /* Check some fuses */ + regmap_read(eb->rmap, IXP43X_EXP_UNIT_FUSE_RESET, &val); + switch (FIELD_GET(IXP43x_EXP_FUSE_SPEED_MASK, val)) { + case 0: + dev_info(dev, "IXP43x at 533 MHz\n"); + break; + case 1: + dev_info(dev, "IXP43x at 400 MHz\n"); + break; + case 2: + dev_info(dev, "IXP43x at 667 MHz\n"); + break; + default: + dev_info(dev, "IXP43x unknown speed\n"); + break; + } + } + + /* Walk over the child nodes and see what chipselects we use */ + for_each_available_child_of_node(np, child) { + ixp4xx_exp_setup_child(eb, child); + /* We have at least one child */ + have_children = true; + } + + if (have_children) + return of_platform_default_populate(np, NULL, dev); + + return 0; +} + +static const struct of_device_id ixp4xx_exp_of_match[] = { + { .compatible = "intel,ixp42x-expansion-bus-controller", }, + { .compatible = "intel,ixp43x-expansion-bus-controller", }, + { .compatible = "intel,ixp45x-expansion-bus-controller", }, + { .compatible = "intel,ixp46x-expansion-bus-controller", }, + { } +}; + +static struct platform_driver ixp4xx_exp_driver = { + .probe = ixp4xx_exp_probe, + .driver = { + .name = "intel-extbus", + .of_match_table = ixp4xx_exp_of_match, + }, +}; +module_platform_driver(ixp4xx_exp_driver); +MODULE_AUTHOR("Linus Walleij "); +MODULE_DESCRIPTION("Intel IXP4xx external bus driver"); +MODULE_LICENSE("GPL"); From 21a0a29d16c672b0c6e3662ea55969dccdbc5547 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Aug 2019 16:38:14 +0200 Subject: [PATCH 3/8] watchdog: ixp4xx: Rewrite driver to use core This rewrites the IXP4xx watchdog driver as follows: - Spawn the watchdog driver as a platform device from the timer driver. It's one device in the hardware, and the fact that Linux splits the handling into two different devices is a Linux pecularity, and thus it becomes a Linux pecularity to spawn a separate watchdog driver. - Spawn the watchdog driver from the timer driver at probe(). This is well after the timer driver as actually registered and started and we know the register base is available. - Instead of looping back callbacks to the timer drivers for all watchdog calls, pass the register base to the watchdog driver and manage the registers there. The two drivers aren't even interested in the same register so the spinlock is totally surplus, delete it. - Replace pretty much all of the content in the watchdog driver with a simple, modern watchdog driver utilizing the watchdog core instead of registering its own misc device and ioctl() handling. - Drop module parameters as the same already exist in the watchdog core. What remains is a slim elegant (IMO) watchdog driver using the watchdog core, spawning from device tree or boardfile alike. Cc: Daniel Lezcano Cc: Thomas Gleixner Reviewed-by: Guenter Roeck Signed-off-by: Linus Walleij --- drivers/clocksource/timer-ixp4xx.c | 48 +++-- drivers/watchdog/Kconfig | 1 + drivers/watchdog/ixp4xx_wdt.c | 315 ++++++++++++----------------- 3 files changed, 171 insertions(+), 193 deletions(-) diff --git a/drivers/clocksource/timer-ixp4xx.c b/drivers/clocksource/timer-ixp4xx.c index 9396745e1c17..cbb184953510 100644 --- a/drivers/clocksource/timer-ixp4xx.c +++ b/drivers/clocksource/timer-ixp4xx.c @@ -18,6 +18,7 @@ #include #include #include +#include /* Goes away with OF conversion */ #include @@ -29,9 +30,6 @@ #define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */ #define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */ #define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */ -#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ -#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ -#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ #define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ /* @@ -45,17 +43,10 @@ #define IXP4XX_OSST_TIMER_1_PEND 0x00000001 #define IXP4XX_OSST_TIMER_2_PEND 0x00000002 #define IXP4XX_OSST_TIMER_TS_PEND 0x00000004 -#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 -#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 - -#define IXP4XX_WDT_KEY 0x0000482E -#define IXP4XX_WDT_RESET_ENABLE 0x00000001 -#define IXP4XX_WDT_IRQ_ENABLE 0x00000002 -#define IXP4XX_WDT_COUNT_ENABLE 0x00000004 +/* Remaining registers are for the watchdog and defined in the watchdog driver */ struct ixp4xx_timer { void __iomem *base; - unsigned int tick_rate; u32 latch; struct clock_event_device clkevt; #ifdef CONFIG_ARM @@ -181,7 +172,6 @@ static __init int ixp4xx_timer_register(void __iomem *base, if (!tmr) return -ENOMEM; tmr->base = base; - tmr->tick_rate = timer_freq; /* * The timer register doesn't allow to specify the two least @@ -239,6 +229,40 @@ static __init int ixp4xx_timer_register(void __iomem *base, return 0; } +static struct platform_device ixp4xx_watchdog_device = { + .name = "ixp4xx-watchdog", + .id = -1, +}; + +/* + * This probe gets called after the timer is already up and running. The main + * function on this platform is to spawn the watchdog device as a child. + */ +static int ixp4xx_timer_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* Pass the base address as platform data and nothing else */ + ixp4xx_watchdog_device.dev.platform_data = local_ixp4xx_timer->base; + ixp4xx_watchdog_device.dev.parent = dev; + return platform_device_register(&ixp4xx_watchdog_device); +} + +static const struct of_device_id ixp4xx_timer_dt_id[] = { + { .compatible = "intel,ixp4xx-timer", }, + { /* sentinel */ }, +}; + +static struct platform_driver ixp4xx_timer_driver = { + .probe = ixp4xx_timer_probe, + .driver = { + .name = "ixp4xx-timer", + .of_match_table = ixp4xx_timer_dt_id, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(ixp4xx_timer_driver); + /** * ixp4xx_timer_setup() - Timer setup function to be called from boardfiles * @timerbase: physical base of timer block diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 546dfc1e2349..0bc7046ab942 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -487,6 +487,7 @@ config FTWDT010_WATCHDOG config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" depends on ARCH_IXP4XX + select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer in the Intel IXP4xx network processors. This driver can diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index aae29dcfaf11..2693ffb24ac7 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -1,220 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/char/watchdog/ixp4xx_wdt.c * * Watchdog driver for Intel IXP4xx network processors * * Author: Deepak Saxena + * Author: Linus Walleij * * Copyright 2004 (c) MontaVista, Software, Inc. * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include -#include #include #include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include -static bool nowayout = WATCHDOG_NOWAYOUT; -static int heartbeat = 60; /* (secs) Default is 1 minute */ -static unsigned long wdt_status; -static unsigned long boot_status; -static DEFINE_SPINLOCK(wdt_lock); - -#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL) - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 - -static void wdt_enable(void) -{ - spin_lock(&wdt_lock); - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat; - *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE; - *IXP4XX_OSWK = 0; - spin_unlock(&wdt_lock); -} - -static void wdt_disable(void) -{ - spin_lock(&wdt_lock); - *IXP4XX_OSWK = IXP4XX_WDT_KEY; - *IXP4XX_OSWE = 0; - *IXP4XX_OSWK = 0; - spin_unlock(&wdt_lock); -} - -static int ixp4xx_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - wdt_enable(); - return stream_open(inode, file); -} - -static ssize_t -ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - return len; -} - -static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | - WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = "IXP4xx Watchdog", +struct ixp4xx_wdt { + struct watchdog_device wdd; + void __iomem *base; + unsigned long rate; }; +/* Fallback if we do not have a clock for this */ +#define IXP4XX_TIMER_FREQ 66666000 -static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +/* Registers after the timer registers */ +#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */ +#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */ +#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */ +#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */ + +#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008 +#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010 +#define IXP4XX_WDT_KEY 0x0000482E +#define IXP4XX_WDT_RESET_ENABLE 0x00000001 +#define IXP4XX_WDT_IRQ_ENABLE 0x00000002 +#define IXP4XX_WDT_COUNT_ENABLE 0x00000004 + +static inline +struct ixp4xx_wdt *to_ixp4xx_wdt(struct watchdog_device *wdd) { - int ret = -ENOTTY; - int time; - - switch (cmd) { - case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, &ident, - sizeof(ident)) ? -EFAULT : 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, (int *)arg); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, (int *)arg); - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - - case WDIOC_SETTIMEOUT: - ret = get_user(time, (int *)arg); - if (ret) - break; - - if (time <= 0 || time > 60) { - ret = -EINVAL; - break; - } - - heartbeat = time; - wdt_enable(); - fallthrough; - - case WDIOC_GETTIMEOUT: - ret = put_user(heartbeat, (int *)arg); - break; - } - return ret; + return container_of(wdd, struct ixp4xx_wdt, wdd); } -static int ixp4xx_wdt_release(struct inode *inode, struct file *file) +static int ixp4xx_wdt_start(struct watchdog_device *wdd) { - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - wdt_disable(); - else - pr_crit("Device closed unexpectedly - timer will not stop\n"); - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd); + + __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(wdd->timeout * iwdt->rate, + iwdt->base + IXP4XX_OSWT_OFFSET); + __raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE, + iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET); return 0; } - -static const struct file_operations ixp4xx_wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = ixp4xx_wdt_write, - .unlocked_ioctl = ixp4xx_wdt_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = ixp4xx_wdt_open, - .release = ixp4xx_wdt_release, -}; - -static struct miscdevice ixp4xx_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ixp4xx_wdt_fops, -}; - -static int __init ixp4xx_wdt_init(void) +static int ixp4xx_wdt_stop(struct watchdog_device *wdd) { + struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd); + + __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET); + __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET); + + return 0; +} + +static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + if (watchdog_active(wdd)) + ixp4xx_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_ops ixp4xx_wdt_ops = { + .start = ixp4xx_wdt_start, + .stop = ixp4xx_wdt_stop, + .set_timeout = ixp4xx_wdt_set_timeout, + .owner = THIS_MODULE, +}; + +static const struct watchdog_info ixp4xx_wdt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT, + .identity = KBUILD_MODNAME, +}; + +/* Devres-handled clock disablement */ +static void ixp4xx_clock_action(void *d) +{ + clk_disable_unprepare(d); +} + +static int ixp4xx_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ixp4xx_wdt *iwdt; + struct clk *clk; int ret; - /* - * FIXME: we bail out on device tree boot but this really needs - * to be fixed in a nicer way: this registers the MDIO bus before - * even matching the driver infrastructure, we should only probe - * detected hardware. - */ - if (of_have_populated_dt()) - return -ENODEV; if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) { - pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n"); - + dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n"); return -ENODEV; } - boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ? - WDIOF_CARDRESET : 0; - ret = misc_register(&ixp4xx_wdt_miscdev); - if (ret == 0) - pr_info("timer heartbeat %d sec\n", heartbeat); - return ret; + + iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL); + if (!iwdt) + return -ENOMEM; + iwdt->base = dev->platform_data; + + /* + * Retrieve rate from a fixed clock from the device tree if + * the parent has that, else use the default clock rate. + */ + clk = devm_clk_get(dev->parent, NULL); + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, ixp4xx_clock_action, clk); + if (ret) + return ret; + iwdt->rate = clk_get_rate(clk); + } + if (!iwdt->rate) + iwdt->rate = IXP4XX_TIMER_FREQ; + + iwdt->wdd.info = &ixp4xx_wdt_info; + iwdt->wdd.ops = &ixp4xx_wdt_ops; + iwdt->wdd.min_timeout = 1; + iwdt->wdd.max_timeout = U32_MAX / iwdt->rate; + iwdt->wdd.parent = dev; + /* Default to 60 seconds */ + iwdt->wdd.timeout = 60U; + watchdog_init_timeout(&iwdt->wdd, 0, dev); + + if (__raw_readl(iwdt->base + IXP4XX_OSST_OFFSET) & + IXP4XX_OSST_TIMER_WARM_RESET) + iwdt->wdd.bootstatus = WDIOF_CARDRESET; + + ret = devm_watchdog_register_device(dev, &iwdt->wdd); + if (ret) + return ret; + + dev_info(dev, "IXP4xx watchdog available\n"); + + return 0; } -static void __exit ixp4xx_wdt_exit(void) -{ - misc_deregister(&ixp4xx_wdt_miscdev); -} - - -module_init(ixp4xx_wdt_init); -module_exit(ixp4xx_wdt_exit); +static struct platform_driver ixp4xx_wdt_driver = { + .probe = ixp4xx_wdt_probe, + .driver = { + .name = "ixp4xx-watchdog", + }, +}; +module_platform_driver(ixp4xx_wdt_driver); MODULE_AUTHOR("Deepak Saxena "); MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog"); - -module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)"); - -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - MODULE_LICENSE("GPL"); From f62b38965ad4cdee29912564f666b9a021d1910d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 11 May 2021 00:54:41 +0200 Subject: [PATCH 4/8] pata: ixp4xx: Use local dev variable Let's simplify all &pdev->dev references by creating a local struct device *dev variable. Signed-off-by: Linus Walleij --- drivers/ata/pata_ixp4xx_cf.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 5881d64af943..23f0f7cacd52 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -139,6 +139,7 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) struct resource *cs0, *cs1; struct ata_host *host; struct ata_port *ap; + struct device *dev = &pdev->dev; struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev); int ret; int irq; @@ -150,17 +151,17 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) return -EINVAL; /* allocate host */ - host = ata_host_alloc(&pdev->dev, 1); + host = ata_host_alloc(dev, 1); if (!host) return -ENOMEM; /* acquire resources and fill host */ - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); if (ret) return ret; - data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000); - data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000); + data->cs0 = devm_ioremap(dev, cs0->start, 0x1000); + data->cs1 = devm_ioremap(dev, cs1->start, 0x1000); if (!data->cs0 || !data->cs1) return -ENOMEM; @@ -185,7 +186,7 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) ixp4xx_setup_port(ap, data, cs0->start, cs1->start); - ata_print_version_once(&pdev->dev, DRV_VERSION); + ata_print_version_once(dev, DRV_VERSION); /* activate host */ return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht); From d2b507acc62d9cdeed0885392ae8c33f8f1a109c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Jul 2021 01:28:05 +0200 Subject: [PATCH 5/8] pata: ixp4xx: Use IS_ENABLED() to determine endianness Instead of an ARM-specific ifdef, use the global CPU config and if (IS_ENABLED()). Signed-off-by: Linus Walleij --- drivers/ata/pata_ixp4xx_cf.c | 41 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 23f0f7cacd52..bc5029d6525d 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -107,29 +107,28 @@ static void ixp4xx_setup_port(struct ata_port *ap, ata_sff_std_ports(ioaddr); -#ifndef __ARMEB__ + if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { + /* adjust the addresses to handle the address swizzling of the + * ixp4xx in little endian mode. + */ - /* adjust the addresses to handle the address swizzling of the - * ixp4xx in little endian mode. - */ + *(unsigned long *)&ioaddr->data_addr ^= 0x02; + *(unsigned long *)&ioaddr->cmd_addr ^= 0x03; + *(unsigned long *)&ioaddr->altstatus_addr ^= 0x03; + *(unsigned long *)&ioaddr->ctl_addr ^= 0x03; + *(unsigned long *)&ioaddr->error_addr ^= 0x03; + *(unsigned long *)&ioaddr->feature_addr ^= 0x03; + *(unsigned long *)&ioaddr->nsect_addr ^= 0x03; + *(unsigned long *)&ioaddr->lbal_addr ^= 0x03; + *(unsigned long *)&ioaddr->lbam_addr ^= 0x03; + *(unsigned long *)&ioaddr->lbah_addr ^= 0x03; + *(unsigned long *)&ioaddr->device_addr ^= 0x03; + *(unsigned long *)&ioaddr->status_addr ^= 0x03; + *(unsigned long *)&ioaddr->command_addr ^= 0x03; - *(unsigned long *)&ioaddr->data_addr ^= 0x02; - *(unsigned long *)&ioaddr->cmd_addr ^= 0x03; - *(unsigned long *)&ioaddr->altstatus_addr ^= 0x03; - *(unsigned long *)&ioaddr->ctl_addr ^= 0x03; - *(unsigned long *)&ioaddr->error_addr ^= 0x03; - *(unsigned long *)&ioaddr->feature_addr ^= 0x03; - *(unsigned long *)&ioaddr->nsect_addr ^= 0x03; - *(unsigned long *)&ioaddr->lbal_addr ^= 0x03; - *(unsigned long *)&ioaddr->lbam_addr ^= 0x03; - *(unsigned long *)&ioaddr->lbah_addr ^= 0x03; - *(unsigned long *)&ioaddr->device_addr ^= 0x03; - *(unsigned long *)&ioaddr->status_addr ^= 0x03; - *(unsigned long *)&ioaddr->command_addr ^= 0x03; - - raw_cmd ^= 0x03; - raw_ctl ^= 0x03; -#endif + raw_cmd ^= 0x03; + raw_ctl ^= 0x03; + } ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl); } From 8e3d25a6231832a9525f0e0bb6fb4c13df347175 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Jul 2021 10:37:55 +0200 Subject: [PATCH 6/8] pata: ixp4xx: Refer to cmd and ctl rather than csN The two "cs0" and "cs1" are "chip selects" but on some platforms such as GW2358 they are actually both in CS3 making this terminology very confusing. Call the addresses "cmd" and "ctl" after function instead. Signed-off-by: Linus Walleij --- drivers/ata/pata_ixp4xx_cf.c | 27 ++++++++++---------- include/linux/platform_data/pata_ixp4xx_cf.h | 4 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index bc5029d6525d..72d6d6f2ef99 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -95,15 +95,14 @@ static struct ata_port_operations ixp4xx_port_ops = { static void ixp4xx_setup_port(struct ata_port *ap, struct ixp4xx_pata_data *data, - unsigned long raw_cs0, unsigned long raw_cs1) + unsigned long raw_cmd, unsigned long raw_ctl) { struct ata_ioports *ioaddr = &ap->ioaddr; - unsigned long raw_cmd = raw_cs0; - unsigned long raw_ctl = raw_cs1 + 0x06; - ioaddr->cmd_addr = data->cs0; - ioaddr->altstatus_addr = data->cs1 + 0x06; - ioaddr->ctl_addr = data->cs1 + 0x06; + raw_ctl += 0x06; + ioaddr->cmd_addr = data->cmd; + ioaddr->altstatus_addr = data->ctl + 0x06; + ioaddr->ctl_addr = data->ctl + 0x06; ata_sff_std_ports(ioaddr); @@ -135,7 +134,7 @@ static void ixp4xx_setup_port(struct ata_port *ap, static int ixp4xx_pata_probe(struct platform_device *pdev) { - struct resource *cs0, *cs1; + struct resource *cmd, *ctl; struct ata_host *host; struct ata_port *ap; struct device *dev = &pdev->dev; @@ -143,10 +142,10 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) int ret; int irq; - cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!cs0 || !cs1) + if (!cmd || !ctl) return -EINVAL; /* allocate host */ @@ -159,10 +158,10 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) if (ret) return ret; - data->cs0 = devm_ioremap(dev, cs0->start, 0x1000); - data->cs1 = devm_ioremap(dev, cs1->start, 0x1000); + data->cmd = devm_ioremap(dev, cmd->start, 0x1000); + data->ctl = devm_ioremap(dev, ctl->start, 0x1000); - if (!data->cs0 || !data->cs1) + if (!data->cmd || !data->ctl) return -ENOMEM; irq = platform_get_irq(pdev, 0); @@ -183,7 +182,7 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) ap->pio_mask = ATA_PIO4; ap->flags |= ATA_FLAG_NO_ATAPI; - ixp4xx_setup_port(ap, data, cs0->start, cs1->start); + ixp4xx_setup_port(ap, data, cmd->start, ctl->start); ata_print_version_once(dev, DRV_VERSION); diff --git a/include/linux/platform_data/pata_ixp4xx_cf.h b/include/linux/platform_data/pata_ixp4xx_cf.h index 601ba97fef57..e60fa41da4a5 100644 --- a/include/linux/platform_data/pata_ixp4xx_cf.h +++ b/include/linux/platform_data/pata_ixp4xx_cf.h @@ -14,8 +14,8 @@ struct ixp4xx_pata_data { volatile u32 *cs1_cfg; unsigned long cs0_bits; unsigned long cs1_bits; - void __iomem *cs0; - void __iomem *cs1; + void __iomem *cmd; + void __iomem *ctl; }; #endif From be470496eece679490dda90ebe497493f4c8faf6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 27 Jul 2021 10:52:10 +0200 Subject: [PATCH 7/8] pata: ixp4xx: Add DT bindings This adds device tree bindings for the Intel IXP4xx compact flash card interface. Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Linus Walleij --- .../ata/intel,ixp4xx-compact-flash.yaml | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml diff --git a/Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml b/Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml new file mode 100644 index 000000000000..52e18600ecff --- /dev/null +++ b/Documentation/devicetree/bindings/ata/intel,ixp4xx-compact-flash.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/intel,ixp4xx-compact-flash.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel IXP4xx CompactFlash Card Controller + +maintainers: + - Linus Walleij + +description: | + The IXP4xx network processors have a CompactFlash interface that presents + a CompactFlash card to the system as a true IDE (parallel ATA) device. The + device is always connected to the expansion bus of the IXP4xx SoCs using one + or two chip select areas and address translating logic on the board. The + node must be placed inside a chip select node on the IXP4xx expansion bus. + +properties: + compatible: + const: intel,ixp4xx-compact-flash + + reg: + items: + - description: Command interface registers + - description: Control interface registers + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: pata-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + bus@c4000000 { + compatible = "intel,ixp43x-expansion-bus-controller", "syscon"; + reg = <0xc4000000 0x1000>; + native-endian; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>; + dma-ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>; + ide@1,0 { + compatible = "intel,ixp4xx-compact-flash"; + reg = <1 0x00000000 0x1000>, <1 0x00040000 0x1000>; + interrupt-parent = <&gpio0>; + interrupts = <12 IRQ_TYPE_EDGE_RISING>; + }; + }; + +... From 47adef20e67d657696c953f4b8023017c6005c1b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Jul 2021 10:44:45 +0200 Subject: [PATCH 8/8] pata: ixp4xx: Rewrite to use device tree This rewrites the IXP4xx CF (IDE) libata driver to use the device tree exclusively to look up its resources: - Probe exclusively from the device tree and look up all resources from there. - Allocate a local state container with devres and pass this around in .private_data. - Initialize with struct ata_port_info. - Use the .set_piomode() callback instead of the much wider .set_mode(), we only support PIO after all. - Bump driver version number from 0.2 to 1.0 to reflect this wider change. - Get a handle on the expansion bus syscon regmap to alter the timings on the chip select. - Put in the more elaborate timing adjustment code for PIO0 to PIO4 in 8 and 16bit mode from the downstream OpenWrt patch. The board file initialization path and platform data include is dropped because the board files will be deleted at the same time as this patch is merged. The platform data file is not deleted right now so as not to conflict with the removal of board files. Signed-off-by: Linus Walleij --- drivers/ata/pata_ixp4xx_cf.c | 201 +++++++++++++++++++++++++++-------- 1 file changed, 154 insertions(+), 47 deletions(-) diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 72d6d6f2ef99..99c63087c8ae 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -13,45 +13,134 @@ */ #include +#include #include #include #include #include -#include +#include #include #define DRV_NAME "pata_ixp4xx_cf" -#define DRV_VERSION "0.2" +#define DRV_VERSION "1.0" -static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error) +struct ixp4xx_pata { + struct ata_host *host; + struct regmap *rmap; + u32 cmd_csreg; + void __iomem *cmd; + void __iomem *ctl; +}; + +#define IXP4XX_EXP_TIMING_STRIDE 0x04 +/* The timings for the chipselect is in bits 29..16 */ +#define IXP4XX_EXP_T1_T5_MASK GENMASK(29, 16) +#define IXP4XX_EXP_PIO_0_8 0x0a470000 +#define IXP4XX_EXP_PIO_1_8 0x06430000 +#define IXP4XX_EXP_PIO_2_8 0x02410000 +#define IXP4XX_EXP_PIO_3_8 0x00820000 +#define IXP4XX_EXP_PIO_4_8 0x00400000 +#define IXP4XX_EXP_PIO_0_16 0x29640000 +#define IXP4XX_EXP_PIO_1_16 0x05030000 +#define IXP4XX_EXP_PIO_2_16 0x00b20000 +#define IXP4XX_EXP_PIO_3_16 0x00820000 +#define IXP4XX_EXP_PIO_4_16 0x00400000 +#define IXP4XX_EXP_BW_MASK (BIT(6)|BIT(0)) +#define IXP4XX_EXP_BYTE_RD16 BIT(6) /* Byte reads on half-word devices */ +#define IXP4XX_EXP_BYTE_EN BIT(0) /* Use 8bit data bus if set */ + +static void ixp4xx_set_8bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode) { - struct ata_device *dev; - - ata_for_each_dev(dev, link, ENABLED) { - ata_dev_info(dev, "configured for PIO0\n"); - dev->pio_mode = XFER_PIO_0; - dev->xfer_mode = XFER_PIO_0; - dev->xfer_shift = ATA_SHIFT_PIO; - dev->flags |= ATA_DFLAG_PIO; + switch (pio_mode) { + case XFER_PIO_0: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_8); + break; + case XFER_PIO_1: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_8); + break; + case XFER_PIO_2: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_8); + break; + case XFER_PIO_3: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_8); + break; + case XFER_PIO_4: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_8); + break; + default: + break; } - return 0; + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16|IXP4XX_EXP_BYTE_EN); } +static void ixp4xx_set_16bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode) +{ + switch (pio_mode){ + case XFER_PIO_0: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_16); + break; + case XFER_PIO_1: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_16); + break; + case XFER_PIO_2: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_16); + break; + case XFER_PIO_3: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_16); + break; + case XFER_PIO_4: + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_16); + break; + default: + break; + } + regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg, + IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16); +} + +/* This sets up the timing on the chipselect CMD accordingly */ +static void ixp4xx_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct ixp4xx_pata *ixpp = ap->host->private_data; + + ata_dev_printk(adev, KERN_INFO, "configured for PIO%d 8bit\n", + adev->pio_mode - XFER_PIO_0); + ixp4xx_set_8bit_timing(ixpp, adev->pio_mode); +} + + static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc, - unsigned char *buf, unsigned int buflen, int rw) + unsigned char *buf, unsigned int buflen, int rw) { unsigned int i; unsigned int words = buflen >> 1; u16 *buf16 = (u16 *) buf; + struct ata_device *adev = qc->dev; struct ata_port *ap = qc->dev->link->ap; void __iomem *mmio = ap->ioaddr.data_addr; - struct ixp4xx_pata_data *data = dev_get_platdata(ap->host->dev); + struct ixp4xx_pata *ixpp = ap->host->private_data; + unsigned long flags; + + ata_dev_printk(adev, KERN_DEBUG, "%s %d bytes\n", (rw == READ) ? "READ" : "WRITE", + buflen); + spin_lock_irqsave(ap->lock, flags); /* set the expansion bus in 16bit mode and restore * 8 bit mode after the transaction. */ - *data->cs0_cfg &= ~(0x01); - udelay(100); + ixp4xx_set_16bit_timing(ixpp, adev->pio_mode); + udelay(5); /* Transfer multiple of 2 bytes */ if (rw == READ) @@ -76,8 +165,10 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc, words++; } - udelay(100); - *data->cs0_cfg |= 0x01; + ixp4xx_set_8bit_timing(ixpp, adev->pio_mode); + udelay(5); + + spin_unlock_irqrestore(ap->lock, flags); return words << 1; } @@ -90,19 +181,25 @@ static struct ata_port_operations ixp4xx_port_ops = { .inherits = &ata_sff_port_ops, .sff_data_xfer = ixp4xx_mmio_data_xfer, .cable_detect = ata_cable_40wire, - .set_mode = ixp4xx_set_mode, + .set_piomode = ixp4xx_set_piomode, +}; + +static struct ata_port_info ixp4xx_port_info = { + .flags = ATA_FLAG_NO_ATAPI, + .pio_mask = ATA_PIO4, + .port_ops = &ixp4xx_port_ops, }; static void ixp4xx_setup_port(struct ata_port *ap, - struct ixp4xx_pata_data *data, + struct ixp4xx_pata *ixpp, unsigned long raw_cmd, unsigned long raw_ctl) { struct ata_ioports *ioaddr = &ap->ioaddr; raw_ctl += 0x06; - ioaddr->cmd_addr = data->cmd; - ioaddr->altstatus_addr = data->ctl + 0x06; - ioaddr->ctl_addr = data->ctl + 0x06; + ioaddr->cmd_addr = ixpp->cmd; + ioaddr->altstatus_addr = ixpp->ctl + 0x06; + ioaddr->ctl_addr = ixpp->ctl + 0x06; ata_sff_std_ports(ioaddr); @@ -135,10 +232,12 @@ static void ixp4xx_setup_port(struct ata_port *ap, static int ixp4xx_pata_probe(struct platform_device *pdev) { struct resource *cmd, *ctl; - struct ata_host *host; - struct ata_port *ap; + struct ata_port_info pi = ixp4xx_port_info; + const struct ata_port_info *ppi[] = { &pi, NULL }; struct device *dev = &pdev->dev; - struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev); + struct device_node *np = dev->of_node; + struct ixp4xx_pata *ixpp; + u32 csindex; int ret; int irq; @@ -148,20 +247,32 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) if (!cmd || !ctl) return -EINVAL; - /* allocate host */ - host = ata_host_alloc(dev, 1); - if (!host) + ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL); + if (!ixpp) return -ENOMEM; - /* acquire resources and fill host */ + ixpp->rmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(ixpp->rmap)) + return dev_err_probe(dev, PTR_ERR(ixpp->rmap), "no regmap\n"); + /* Inspect our address to figure out what chipselect the CMD is on */ + ret = of_property_read_u32_index(np, "reg", 0, &csindex); + if (ret) + return dev_err_probe(dev, ret, "can't inspect CMD address\n"); + dev_info(dev, "using CS%d for PIO timing configuration\n", csindex); + ixpp->cmd_csreg = csindex * IXP4XX_EXP_TIMING_STRIDE; + + ixpp->host = ata_host_alloc_pinfo(dev, ppi, 1); + if (!ixpp->host) + return -ENOMEM; + ixpp->host->private_data = ixpp; + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); if (ret) return ret; - data->cmd = devm_ioremap(dev, cmd->start, 0x1000); - data->ctl = devm_ioremap(dev, ctl->start, 0x1000); - - if (!data->cmd || !data->ctl) + ixpp->cmd = devm_ioremap_resource(dev, cmd); + ixpp->ctl = devm_ioremap_resource(dev, ctl); + if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl)) return -ENOMEM; irq = platform_get_irq(pdev, 0); @@ -172,27 +283,23 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) else return -EINVAL; - /* Setup expansion bus chip selects */ - *data->cs0_cfg = data->cs0_bits; - *data->cs1_cfg = data->cs1_bits; - - ap = host->ports[0]; - - ap->ops = &ixp4xx_port_ops; - ap->pio_mask = ATA_PIO4; - ap->flags |= ATA_FLAG_NO_ATAPI; - - ixp4xx_setup_port(ap, data, cmd->start, ctl->start); + /* Just one port to set up */ + ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start); ata_print_version_once(dev, DRV_VERSION); - /* activate host */ - return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht); + return ata_host_activate(ixpp->host, irq, ata_sff_interrupt, 0, &ixp4xx_sht); } +static const struct of_device_id ixp4xx_pata_of_match[] = { + { .compatible = "intel,ixp4xx-compact-flash", }, + { }, +}; + static struct platform_driver ixp4xx_pata_platform_driver = { .driver = { .name = DRV_NAME, + .of_match_table = ixp4xx_pata_of_match, }, .probe = ixp4xx_pata_probe, .remove = ata_platform_remove_one,