| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2018 Rafał Miłecki <rafal@milecki.pl> | |
| */ | |
| #include <linux/err.h> | |
| #include <linux/io.h> | |
| #include <linux/module.h> | |
| #include <linux/of.h> | |
| #include <linux/pinctrl/pinconf-generic.h> | |
| #include <linux/pinctrl/pinctrl.h> | |
| #include <linux/pinctrl/pinmux.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/property.h> | |
| #include <linux/slab.h> | |
| #include "../core.h" | |
| #include "../pinmux.h" | |
| #define FLAG_BCM4708 BIT(1) | |
| #define FLAG_BCM4709 BIT(2) | |
| #define FLAG_BCM53012 BIT(3) | |
| struct ns_pinctrl { | |
| struct device *dev; | |
| unsigned int chipset_flag; | |
| struct pinctrl_dev *pctldev; | |
| void __iomem *base; | |
| struct pinctrl_desc pctldesc; | |
| }; | |
| /* | |
| * Pins | |
| */ | |
| static const struct pinctrl_pin_desc ns_pinctrl_pins[] = { | |
| { 0, "spi_clk", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 1, "spi_ss", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 2, "spi_mosi", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 3, "spi_miso", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 4, "i2c_scl", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 5, "i2c_sda", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 6, "mdc", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 7, "mdio", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 8, "pwm0", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 9, "pwm1", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 10, "pwm2", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 11, "pwm3", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 12, "uart1_rx", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 13, "uart1_tx", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 14, "uart1_cts", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 15, "uart1_rts", (void *)(FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 16, "uart2_rx", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 17, "uart2_tx", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| /* TODO { ??, "xtal_out", (void *)(FLAG_BCM4709) }, */ | |
| { 22, "sdio_pwr", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| { 23, "sdio_en_1p8v", (void *)(FLAG_BCM4709 | FLAG_BCM53012) }, | |
| }; | |
| /* | |
| * Groups | |
| */ | |
| struct ns_pinctrl_group { | |
| const char *name; | |
| unsigned int *pins; | |
| const unsigned int num_pins; | |
| unsigned int chipsets; | |
| }; | |
| static unsigned int spi_pins[] = { 0, 1, 2, 3 }; | |
| static unsigned int i2c_pins[] = { 4, 5 }; | |
| static unsigned int mdio_pins[] = { 6, 7 }; | |
| static unsigned int pwm0_pins[] = { 8 }; | |
| static unsigned int pwm1_pins[] = { 9 }; | |
| static unsigned int pwm2_pins[] = { 10 }; | |
| static unsigned int pwm3_pins[] = { 11 }; | |
| static unsigned int uart1_pins[] = { 12, 13, 14, 15 }; | |
| static unsigned int uart2_pins[] = { 16, 17 }; | |
| static unsigned int sdio_pwr_pins[] = { 22 }; | |
| static unsigned int sdio_1p8v_pins[] = { 23 }; | |
| #define NS_GROUP(_name, _pins, _chipsets) \ | |
| { \ | |
| .name = _name, \ | |
| .pins = _pins, \ | |
| .num_pins = ARRAY_SIZE(_pins), \ | |
| .chipsets = _chipsets, \ | |
| } | |
| static const struct ns_pinctrl_group ns_pinctrl_groups[] = { | |
| NS_GROUP("spi_grp", spi_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("i2c_grp", i2c_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("mdio_grp", mdio_pins, FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("pwm0_grp", pwm0_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("pwm1_grp", pwm1_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("pwm2_grp", pwm2_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("pwm3_grp", pwm3_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("uart1_grp", uart1_pins, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("uart2_grp", uart2_pins, FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("sdio_pwr_grp", sdio_pwr_pins, FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_GROUP("sdio_1p8v_grp", sdio_1p8v_pins, FLAG_BCM4709 | FLAG_BCM53012), | |
| }; | |
| /* | |
| * Functions | |
| */ | |
| struct ns_pinctrl_function { | |
| const char *name; | |
| const char * const *groups; | |
| const unsigned int num_groups; | |
| unsigned int chipsets; | |
| }; | |
| static const char * const spi_groups[] = { "spi_grp" }; | |
| static const char * const i2c_groups[] = { "i2c_grp" }; | |
| static const char * const mdio_groups[] = { "mdio_grp" }; | |
| static const char * const pwm_groups[] = { "pwm0_grp", "pwm1_grp", "pwm2_grp", | |
| "pwm3_grp" }; | |
| static const char * const uart1_groups[] = { "uart1_grp" }; | |
| static const char * const uart2_groups[] = { "uart2_grp" }; | |
| static const char * const sdio_groups[] = { "sdio_pwr_grp", "sdio_1p8v_grp" }; | |
| #define NS_FUNCTION(_name, _groups, _chipsets) \ | |
| { \ | |
| .name = _name, \ | |
| .groups = _groups, \ | |
| .num_groups = ARRAY_SIZE(_groups), \ | |
| .chipsets = _chipsets, \ | |
| } | |
| static const struct ns_pinctrl_function ns_pinctrl_functions[] = { | |
| NS_FUNCTION("spi", spi_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("i2c", i2c_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("mdio", mdio_groups, FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("pwm", pwm_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("uart1", uart1_groups, FLAG_BCM4708 | FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("uart2", uart2_groups, FLAG_BCM4709 | FLAG_BCM53012), | |
| NS_FUNCTION("sdio", sdio_groups, FLAG_BCM4709 | FLAG_BCM53012), | |
| }; | |
| /* | |
| * Groups code | |
| */ | |
| static const struct pinctrl_ops ns_pinctrl_ops = { | |
| .get_groups_count = pinctrl_generic_get_group_count, | |
| .get_group_name = pinctrl_generic_get_group_name, | |
| .get_group_pins = pinctrl_generic_get_group_pins, | |
| .dt_node_to_map = pinconf_generic_dt_node_to_map_group, | |
| .dt_free_map = pinconf_generic_dt_free_map, | |
| }; | |
| /* | |
| * Functions code | |
| */ | |
| static int ns_pinctrl_set_mux(struct pinctrl_dev *pctrl_dev, | |
| unsigned int func_select, | |
| unsigned int group_selector) | |
| { | |
| struct ns_pinctrl *ns_pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); | |
| struct group_desc *group; | |
| u32 unset = 0; | |
| u32 tmp; | |
| int i; | |
| group = pinctrl_generic_get_group(pctrl_dev, group_selector); | |
| if (!group) | |
| return -EINVAL; | |
| for (i = 0; i < group->grp.npins; i++) | |
| unset |= BIT(group->grp.pins[i]); | |
| tmp = readl(ns_pinctrl->base); | |
| tmp &= ~unset; | |
| writel(tmp, ns_pinctrl->base); | |
| return 0; | |
| } | |
| static const struct pinmux_ops ns_pinctrl_pmxops = { | |
| .get_functions_count = pinmux_generic_get_function_count, | |
| .get_function_name = pinmux_generic_get_function_name, | |
| .get_function_groups = pinmux_generic_get_function_groups, | |
| .set_mux = ns_pinctrl_set_mux, | |
| }; | |
| /* | |
| * Controller code | |
| */ | |
| static const struct pinctrl_desc ns_pinctrl_desc = { | |
| .name = "pinctrl-ns", | |
| .pctlops = &ns_pinctrl_ops, | |
| .pmxops = &ns_pinctrl_pmxops, | |
| }; | |
| static const struct of_device_id ns_pinctrl_of_match_table[] = { | |
| { .compatible = "brcm,bcm4708-pinmux", .data = (void *)FLAG_BCM4708, }, | |
| { .compatible = "brcm,bcm4709-pinmux", .data = (void *)FLAG_BCM4709, }, | |
| { .compatible = "brcm,bcm53012-pinmux", .data = (void *)FLAG_BCM53012, }, | |
| { } | |
| }; | |
| MODULE_DEVICE_TABLE(of, ns_pinctrl_of_match_table); | |
| static int ns_pinctrl_probe(struct platform_device *pdev) | |
| { | |
| struct device *dev = &pdev->dev; | |
| struct ns_pinctrl *ns_pinctrl; | |
| struct pinctrl_desc *pctldesc; | |
| struct pinctrl_pin_desc *pin; | |
| struct resource *res; | |
| int i; | |
| ns_pinctrl = devm_kzalloc(dev, sizeof(*ns_pinctrl), GFP_KERNEL); | |
| if (!ns_pinctrl) | |
| return -ENOMEM; | |
| pctldesc = &ns_pinctrl->pctldesc; | |
| platform_set_drvdata(pdev, ns_pinctrl); | |
| /* Set basic properties */ | |
| ns_pinctrl->dev = dev; | |
| ns_pinctrl->chipset_flag = (uintptr_t)device_get_match_data(dev); | |
| res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
| "cru_gpio_control"); | |
| ns_pinctrl->base = devm_ioremap_resource(dev, res); | |
| if (IS_ERR(ns_pinctrl->base)) | |
| return PTR_ERR(ns_pinctrl->base); | |
| memcpy(pctldesc, &ns_pinctrl_desc, sizeof(*pctldesc)); | |
| /* Set pinctrl properties */ | |
| pctldesc->pins = devm_kcalloc(dev, ARRAY_SIZE(ns_pinctrl_pins), | |
| sizeof(struct pinctrl_pin_desc), | |
| GFP_KERNEL); | |
| if (!pctldesc->pins) | |
| return -ENOMEM; | |
| for (i = 0, pin = (struct pinctrl_pin_desc *)&pctldesc->pins[0]; | |
| i < ARRAY_SIZE(ns_pinctrl_pins); i++) { | |
| const struct pinctrl_pin_desc *src = &ns_pinctrl_pins[i]; | |
| unsigned int chipsets = (uintptr_t)src->drv_data; | |
| if (chipsets & ns_pinctrl->chipset_flag) { | |
| memcpy(pin++, src, sizeof(*src)); | |
| pctldesc->npins++; | |
| } | |
| } | |
| /* Register */ | |
| ns_pinctrl->pctldev = devm_pinctrl_register(dev, pctldesc, ns_pinctrl); | |
| if (IS_ERR(ns_pinctrl->pctldev)) { | |
| dev_err(dev, "Failed to register pinctrl\n"); | |
| return PTR_ERR(ns_pinctrl->pctldev); | |
| } | |
| for (i = 0; i < ARRAY_SIZE(ns_pinctrl_groups); i++) { | |
| const struct ns_pinctrl_group *group = &ns_pinctrl_groups[i]; | |
| if (!(group->chipsets & ns_pinctrl->chipset_flag)) | |
| continue; | |
| pinctrl_generic_add_group(ns_pinctrl->pctldev, group->name, | |
| group->pins, group->num_pins, NULL); | |
| } | |
| for (i = 0; i < ARRAY_SIZE(ns_pinctrl_functions); i++) { | |
| const struct ns_pinctrl_function *function = &ns_pinctrl_functions[i]; | |
| if (!(function->chipsets & ns_pinctrl->chipset_flag)) | |
| continue; | |
| pinmux_generic_add_function(ns_pinctrl->pctldev, function->name, | |
| function->groups, | |
| function->num_groups, NULL); | |
| } | |
| return 0; | |
| } | |
| static struct platform_driver ns_pinctrl_driver = { | |
| .probe = ns_pinctrl_probe, | |
| .driver = { | |
| .name = "ns-pinmux", | |
| .of_match_table = ns_pinctrl_of_match_table, | |
| }, | |
| }; | |
| module_platform_driver(ns_pinctrl_driver); | |
| MODULE_AUTHOR("Rafał Miłecki"); |