root/drivers/bus/intel-ixp4xx-eb.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Intel IXP4xx Expansion Bus Controller
 * Copyright (C) 2021 Linaro Ltd.
 *
 * Author: Linus Walleij <linus.walleij@linaro.org>
 */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/log2.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

#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(29, 28)
#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, dangerous to touch on IXP42x */
#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 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_43x) {
                /* Should always be zero */
                cs_cfg &= ~IXP4XX_EXP_WORD;
                /*
                 * 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)
                return 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 <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("Intel IXP4xx external bus driver");