601 lines
14 KiB
C
601 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2018 Google LLC
|
|
* Copyright (c) 2021 Aspeed Technology Inc.
|
|
*/
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
/* register offsets */
|
|
#define HICR9 0x98
|
|
#define HICRA 0x9c
|
|
|
|
/* attributes options */
|
|
#define UART_ROUTING_IO1 "io1"
|
|
#define UART_ROUTING_IO2 "io2"
|
|
#define UART_ROUTING_IO3 "io3"
|
|
#define UART_ROUTING_IO4 "io4"
|
|
#define UART_ROUTING_IO5 "io5"
|
|
#define UART_ROUTING_IO6 "io6"
|
|
#define UART_ROUTING_IO10 "io10"
|
|
#define UART_ROUTING_UART1 "uart1"
|
|
#define UART_ROUTING_UART2 "uart2"
|
|
#define UART_ROUTING_UART3 "uart3"
|
|
#define UART_ROUTING_UART4 "uart4"
|
|
#define UART_ROUTING_UART5 "uart5"
|
|
#define UART_ROUTING_UART6 "uart6"
|
|
#define UART_ROUTING_UART10 "uart10"
|
|
#define UART_ROUTING_RES "reserved"
|
|
|
|
struct aspeed_uart_routing {
|
|
struct regmap *map;
|
|
struct attribute_group const *attr_grp;
|
|
};
|
|
|
|
struct aspeed_uart_routing_selector {
|
|
struct device_attribute dev_attr;
|
|
uint8_t reg;
|
|
uint8_t mask;
|
|
uint8_t shift;
|
|
const char *const options[];
|
|
};
|
|
|
|
#define to_routing_selector(_dev_attr) \
|
|
container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr)
|
|
|
|
static ssize_t aspeed_uart_routing_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf);
|
|
|
|
static ssize_t aspeed_uart_routing_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count);
|
|
|
|
#define ROUTING_ATTR(_name) { \
|
|
.attr = {.name = _name, \
|
|
.mode = VERIFY_OCTAL_PERMISSIONS(0644) }, \
|
|
.show = aspeed_uart_routing_show, \
|
|
.store = aspeed_uart_routing_store, \
|
|
}
|
|
|
|
/* routing selector for AST25xx */
|
|
static struct aspeed_uart_routing_selector ast2500_io6_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO6),
|
|
.reg = HICR9,
|
|
.shift = 8,
|
|
.mask = 0xf,
|
|
.options = {
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO5,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_uart5_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART5),
|
|
.reg = HICRA,
|
|
.shift = 28,
|
|
.mask = 0xf,
|
|
.options = {
|
|
UART_ROUTING_IO5,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_uart4_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
|
|
.reg = HICRA,
|
|
.shift = 25,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_uart3_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
|
|
.reg = HICRA,
|
|
.shift = 22,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_uart2_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
|
|
.reg = HICRA,
|
|
.shift = 19,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_uart1_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
|
|
.reg = HICRA,
|
|
.shift = 16,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_io5_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO5),
|
|
.reg = HICRA,
|
|
.shift = 12,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_io4_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
|
|
.reg = HICRA,
|
|
.shift = 9,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_io3_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
|
|
.reg = HICRA,
|
|
.shift = 6,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_io2_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
|
|
.reg = HICRA,
|
|
.shift = 3,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2500_io1_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
|
|
.reg = HICRA,
|
|
.shift = 0,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART5,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO6,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct attribute *ast2500_uart_routing_attrs[] = {
|
|
&ast2500_io6_sel.dev_attr.attr,
|
|
&ast2500_uart5_sel.dev_attr.attr,
|
|
&ast2500_uart4_sel.dev_attr.attr,
|
|
&ast2500_uart3_sel.dev_attr.attr,
|
|
&ast2500_uart2_sel.dev_attr.attr,
|
|
&ast2500_uart1_sel.dev_attr.attr,
|
|
&ast2500_io5_sel.dev_attr.attr,
|
|
&ast2500_io4_sel.dev_attr.attr,
|
|
&ast2500_io3_sel.dev_attr.attr,
|
|
&ast2500_io2_sel.dev_attr.attr,
|
|
&ast2500_io1_sel.dev_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group ast2500_uart_routing_attr_group = {
|
|
.attrs = ast2500_uart_routing_attrs,
|
|
};
|
|
|
|
/* routing selector for AST26xx */
|
|
static struct aspeed_uart_routing_selector ast2600_uart10_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART10),
|
|
.reg = HICR9,
|
|
.shift = 12,
|
|
.mask = 0xf,
|
|
.options = {
|
|
UART_ROUTING_IO10,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_RES,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_io10_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO10),
|
|
.reg = HICR9,
|
|
.shift = 8,
|
|
.mask = 0xf,
|
|
.options = {
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_RES,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_RES,
|
|
UART_ROUTING_UART10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_uart4_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART4),
|
|
.reg = HICRA,
|
|
.shift = 25,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_uart3_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART3),
|
|
.reg = HICRA,
|
|
.shift = 22,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_uart2_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART2),
|
|
.reg = HICRA,
|
|
.shift = 19,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_uart1_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_UART1),
|
|
.reg = HICRA,
|
|
.shift = 16,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_io4_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO4),
|
|
.reg = HICRA,
|
|
.shift = 9,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART10,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_io3_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO3),
|
|
.reg = HICRA,
|
|
.shift = 6,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART10,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_IO1,
|
|
UART_ROUTING_IO2,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_io2_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO2),
|
|
.reg = HICRA,
|
|
.shift = 3,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART10,
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct aspeed_uart_routing_selector ast2600_io1_sel = {
|
|
.dev_attr = ROUTING_ATTR(UART_ROUTING_IO1),
|
|
.reg = HICRA,
|
|
.shift = 0,
|
|
.mask = 0x7,
|
|
.options = {
|
|
UART_ROUTING_UART1,
|
|
UART_ROUTING_UART2,
|
|
UART_ROUTING_UART3,
|
|
UART_ROUTING_UART4,
|
|
UART_ROUTING_UART10,
|
|
UART_ROUTING_IO3,
|
|
UART_ROUTING_IO4,
|
|
UART_ROUTING_IO10,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
static struct attribute *ast2600_uart_routing_attrs[] = {
|
|
&ast2600_uart10_sel.dev_attr.attr,
|
|
&ast2600_io10_sel.dev_attr.attr,
|
|
&ast2600_uart4_sel.dev_attr.attr,
|
|
&ast2600_uart3_sel.dev_attr.attr,
|
|
&ast2600_uart2_sel.dev_attr.attr,
|
|
&ast2600_uart1_sel.dev_attr.attr,
|
|
&ast2600_io4_sel.dev_attr.attr,
|
|
&ast2600_io3_sel.dev_attr.attr,
|
|
&ast2600_io2_sel.dev_attr.attr,
|
|
&ast2600_io1_sel.dev_attr.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group ast2600_uart_routing_attr_group = {
|
|
.attrs = ast2600_uart_routing_attrs,
|
|
};
|
|
|
|
static ssize_t aspeed_uart_routing_show(struct device *dev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
|
|
struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
|
|
int val, pos, len;
|
|
|
|
regmap_read(uart_routing->map, sel->reg, &val);
|
|
val = (val >> sel->shift) & sel->mask;
|
|
|
|
len = 0;
|
|
for (pos = 0; sel->options[pos] != NULL; ++pos) {
|
|
if (pos == val)
|
|
len += sysfs_emit_at(buf, len, "[%s] ", sel->options[pos]);
|
|
else
|
|
len += sysfs_emit_at(buf, len, "%s ", sel->options[pos]);
|
|
}
|
|
|
|
if (val >= pos)
|
|
len += sysfs_emit_at(buf, len, "[unknown(%d)]", val);
|
|
|
|
len += sysfs_emit_at(buf, len, "\n");
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t aspeed_uart_routing_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
|
|
struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
|
|
int val;
|
|
|
|
val = __sysfs_match_string(sel->options, -1, buf);
|
|
if (val < 0) {
|
|
dev_err(dev, "invalid value \"%s\"\n", buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
regmap_update_bits(uart_routing->map, sel->reg,
|
|
(sel->mask << sel->shift),
|
|
(val & sel->mask) << sel->shift);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int aspeed_uart_routing_probe(struct platform_device *pdev)
|
|
{
|
|
int rc;
|
|
struct device *dev = &pdev->dev;
|
|
struct aspeed_uart_routing *uart_routing;
|
|
|
|
uart_routing = devm_kzalloc(&pdev->dev, sizeof(*uart_routing), GFP_KERNEL);
|
|
if (!uart_routing)
|
|
return -ENOMEM;
|
|
|
|
uart_routing->map = syscon_node_to_regmap(dev->parent->of_node);
|
|
if (IS_ERR(uart_routing->map)) {
|
|
dev_err(dev, "cannot get regmap\n");
|
|
return PTR_ERR(uart_routing->map);
|
|
}
|
|
|
|
uart_routing->attr_grp = of_device_get_match_data(dev);
|
|
|
|
rc = sysfs_create_group(&dev->kobj, uart_routing->attr_grp);
|
|
if (rc < 0)
|
|
return rc;
|
|
|
|
dev_set_drvdata(dev, uart_routing);
|
|
|
|
dev_info(dev, "module loaded\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aspeed_uart_routing_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
|
|
|
|
sysfs_remove_group(&dev->kobj, uart_routing->attr_grp);
|
|
}
|
|
|
|
static const struct of_device_id aspeed_uart_routing_table[] = {
|
|
{ .compatible = "aspeed,ast2400-uart-routing",
|
|
.data = &ast2500_uart_routing_attr_group },
|
|
{ .compatible = "aspeed,ast2500-uart-routing",
|
|
.data = &ast2500_uart_routing_attr_group },
|
|
{ .compatible = "aspeed,ast2600-uart-routing",
|
|
.data = &ast2600_uart_routing_attr_group },
|
|
{ },
|
|
};
|
|
|
|
static struct platform_driver aspeed_uart_routing_driver = {
|
|
.driver = {
|
|
.name = "aspeed-uart-routing",
|
|
.of_match_table = aspeed_uart_routing_table,
|
|
},
|
|
.probe = aspeed_uart_routing_probe,
|
|
.remove_new = aspeed_uart_routing_remove,
|
|
};
|
|
|
|
module_platform_driver(aspeed_uart_routing_driver);
|
|
|
|
MODULE_AUTHOR("Oskar Senft <osk@google.com>");
|
|
MODULE_AUTHOR("Chia-Wei Wang <chiawei_wang@aspeedtech.com>");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("Driver to configure Aspeed UART routing");
|