162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2020, Broadcom */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/init.h> 562306a36Sopenharmony_ci#include <linux/types.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/kdebug.h> 1462306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct out_pin { 1762306a36Sopenharmony_ci u32 enable_mask; 1862306a36Sopenharmony_ci u32 value_mask; 1962306a36Sopenharmony_ci u32 changed_mask; 2062306a36Sopenharmony_ci u32 clr_changed_mask; 2162306a36Sopenharmony_ci struct gpio_desc *gpiod; 2262306a36Sopenharmony_ci const char *name; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct in_pin { 2662306a36Sopenharmony_ci u32 enable_mask; 2762306a36Sopenharmony_ci u32 value_mask; 2862306a36Sopenharmony_ci struct gpio_desc *gpiod; 2962306a36Sopenharmony_ci const char *name; 3062306a36Sopenharmony_ci struct brcmstb_usb_pinmap_data *pdata; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct brcmstb_usb_pinmap_data { 3462306a36Sopenharmony_ci void __iomem *regs; 3562306a36Sopenharmony_ci int in_count; 3662306a36Sopenharmony_ci struct in_pin *in_pins; 3762306a36Sopenharmony_ci int out_count; 3862306a36Sopenharmony_ci struct out_pin *out_pins; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void pinmap_set(void __iomem *reg, u32 mask) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 val; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci val = readl(reg); 4762306a36Sopenharmony_ci val |= mask; 4862306a36Sopenharmony_ci writel(val, reg); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void pinmap_unset(void __iomem *reg, u32 mask) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci u32 val; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci val = readl(reg); 5662306a36Sopenharmony_ci val &= ~mask; 5762306a36Sopenharmony_ci writel(val, reg); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void sync_in_pin(struct in_pin *pin) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci u32 val; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci val = gpiod_get_value(pin->gpiod); 6562306a36Sopenharmony_ci if (val) 6662306a36Sopenharmony_ci pinmap_set(pin->pdata->regs, pin->value_mask); 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci pinmap_unset(pin->pdata->regs, pin->value_mask); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * Interrupt from override register, propagate from override bit 7362306a36Sopenharmony_ci * to GPIO. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic irqreturn_t brcmstb_usb_pinmap_ovr_isr(int irq, void *dev_id) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct brcmstb_usb_pinmap_data *pdata = dev_id; 7862306a36Sopenharmony_ci struct out_pin *pout; 7962306a36Sopenharmony_ci u32 val; 8062306a36Sopenharmony_ci u32 bit; 8162306a36Sopenharmony_ci int x; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci pr_debug("%s: reg: 0x%x\n", __func__, readl(pdata->regs)); 8462306a36Sopenharmony_ci pout = pdata->out_pins; 8562306a36Sopenharmony_ci for (x = 0; x < pdata->out_count; x++) { 8662306a36Sopenharmony_ci val = readl(pdata->regs); 8762306a36Sopenharmony_ci if (val & pout->changed_mask) { 8862306a36Sopenharmony_ci pinmap_set(pdata->regs, pout->clr_changed_mask); 8962306a36Sopenharmony_ci pinmap_unset(pdata->regs, pout->clr_changed_mask); 9062306a36Sopenharmony_ci bit = val & pout->value_mask; 9162306a36Sopenharmony_ci gpiod_set_value(pout->gpiod, bit ? 1 : 0); 9262306a36Sopenharmony_ci pr_debug("%s: %s bit changed state to %d\n", 9362306a36Sopenharmony_ci __func__, pout->name, bit ? 1 : 0); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci return IRQ_HANDLED; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * Interrupt from GPIO, propagate from GPIO to override bit. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic irqreturn_t brcmstb_usb_pinmap_gpio_isr(int irq, void *dev_id) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct in_pin *pin = dev_id; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci pr_debug("%s: %s pin changed state\n", __func__, pin->name); 10762306a36Sopenharmony_ci sync_in_pin(pin); 10862306a36Sopenharmony_ci return IRQ_HANDLED; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void get_pin_counts(struct device_node *dn, int *in_count, 11362306a36Sopenharmony_ci int *out_count) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int in; 11662306a36Sopenharmony_ci int out; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci *in_count = 0; 11962306a36Sopenharmony_ci *out_count = 0; 12062306a36Sopenharmony_ci in = of_property_count_strings(dn, "brcm,in-functions"); 12162306a36Sopenharmony_ci if (in < 0) 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci out = of_property_count_strings(dn, "brcm,out-functions"); 12462306a36Sopenharmony_ci if (out < 0) 12562306a36Sopenharmony_ci return; 12662306a36Sopenharmony_ci *in_count = in; 12762306a36Sopenharmony_ci *out_count = out; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int parse_pins(struct device *dev, struct device_node *dn, 13162306a36Sopenharmony_ci struct brcmstb_usb_pinmap_data *pdata) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct out_pin *pout; 13462306a36Sopenharmony_ci struct in_pin *pin; 13562306a36Sopenharmony_ci int index; 13662306a36Sopenharmony_ci int res; 13762306a36Sopenharmony_ci int x; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci pin = pdata->in_pins; 14062306a36Sopenharmony_ci for (x = 0, index = 0; x < pdata->in_count; x++) { 14162306a36Sopenharmony_ci pin->gpiod = devm_gpiod_get_index(dev, "in", x, GPIOD_IN); 14262306a36Sopenharmony_ci if (IS_ERR(pin->gpiod)) { 14362306a36Sopenharmony_ci dev_err(dev, "Error getting gpio %s\n", pin->name); 14462306a36Sopenharmony_ci return PTR_ERR(pin->gpiod); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci res = of_property_read_string_index(dn, "brcm,in-functions", x, 14862306a36Sopenharmony_ci &pin->name); 14962306a36Sopenharmony_ci if (res < 0) { 15062306a36Sopenharmony_ci dev_err(dev, "Error getting brcm,in-functions for %s\n", 15162306a36Sopenharmony_ci pin->name); 15262306a36Sopenharmony_ci return res; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,in-masks", index++, 15562306a36Sopenharmony_ci &pin->enable_mask); 15662306a36Sopenharmony_ci if (res < 0) { 15762306a36Sopenharmony_ci dev_err(dev, "Error getting 1st brcm,in-masks for %s\n", 15862306a36Sopenharmony_ci pin->name); 15962306a36Sopenharmony_ci return res; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,in-masks", index++, 16262306a36Sopenharmony_ci &pin->value_mask); 16362306a36Sopenharmony_ci if (res < 0) { 16462306a36Sopenharmony_ci dev_err(dev, "Error getting 2nd brcm,in-masks for %s\n", 16562306a36Sopenharmony_ci pin->name); 16662306a36Sopenharmony_ci return res; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci pin->pdata = pdata; 16962306a36Sopenharmony_ci pin++; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci pout = pdata->out_pins; 17262306a36Sopenharmony_ci for (x = 0, index = 0; x < pdata->out_count; x++) { 17362306a36Sopenharmony_ci pout->gpiod = devm_gpiod_get_index(dev, "out", x, 17462306a36Sopenharmony_ci GPIOD_OUT_HIGH); 17562306a36Sopenharmony_ci if (IS_ERR(pout->gpiod)) { 17662306a36Sopenharmony_ci dev_err(dev, "Error getting gpio %s\n", pin->name); 17762306a36Sopenharmony_ci return PTR_ERR(pout->gpiod); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci res = of_property_read_string_index(dn, "brcm,out-functions", x, 18062306a36Sopenharmony_ci &pout->name); 18162306a36Sopenharmony_ci if (res < 0) { 18262306a36Sopenharmony_ci dev_err(dev, "Error getting brcm,out-functions for %s\n", 18362306a36Sopenharmony_ci pout->name); 18462306a36Sopenharmony_ci return res; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 18762306a36Sopenharmony_ci &pout->enable_mask); 18862306a36Sopenharmony_ci if (res < 0) { 18962306a36Sopenharmony_ci dev_err(dev, "Error getting 1st brcm,out-masks for %s\n", 19062306a36Sopenharmony_ci pout->name); 19162306a36Sopenharmony_ci return res; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 19462306a36Sopenharmony_ci &pout->value_mask); 19562306a36Sopenharmony_ci if (res < 0) { 19662306a36Sopenharmony_ci dev_err(dev, "Error getting 2nd brcm,out-masks for %s\n", 19762306a36Sopenharmony_ci pout->name); 19862306a36Sopenharmony_ci return res; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 20162306a36Sopenharmony_ci &pout->changed_mask); 20262306a36Sopenharmony_ci if (res < 0) { 20362306a36Sopenharmony_ci dev_err(dev, "Error getting 3rd brcm,out-masks for %s\n", 20462306a36Sopenharmony_ci pout->name); 20562306a36Sopenharmony_ci return res; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 20862306a36Sopenharmony_ci &pout->clr_changed_mask); 20962306a36Sopenharmony_ci if (res < 0) { 21062306a36Sopenharmony_ci dev_err(dev, "Error getting 4th out-masks for %s\n", 21162306a36Sopenharmony_ci pout->name); 21262306a36Sopenharmony_ci return res; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci pout++; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void sync_all_pins(struct brcmstb_usb_pinmap_data *pdata) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct out_pin *pout; 22262306a36Sopenharmony_ci struct in_pin *pin; 22362306a36Sopenharmony_ci int val; 22462306a36Sopenharmony_ci int x; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Enable the override, clear any changed condition and 22862306a36Sopenharmony_ci * propagate the state to the GPIO for all out pins. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci pout = pdata->out_pins; 23162306a36Sopenharmony_ci for (x = 0; x < pdata->out_count; x++) { 23262306a36Sopenharmony_ci pinmap_set(pdata->regs, pout->enable_mask); 23362306a36Sopenharmony_ci pinmap_set(pdata->regs, pout->clr_changed_mask); 23462306a36Sopenharmony_ci pinmap_unset(pdata->regs, pout->clr_changed_mask); 23562306a36Sopenharmony_ci val = readl(pdata->regs) & pout->value_mask; 23662306a36Sopenharmony_ci gpiod_set_value(pout->gpiod, val ? 1 : 0); 23762306a36Sopenharmony_ci pout++; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* sync and enable all in pins. */ 24162306a36Sopenharmony_ci pin = pdata->in_pins; 24262306a36Sopenharmony_ci for (x = 0; x < pdata->in_count; x++) { 24362306a36Sopenharmony_ci sync_in_pin(pin); 24462306a36Sopenharmony_ci pinmap_set(pdata->regs, pin->enable_mask); 24562306a36Sopenharmony_ci pin++; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 25262306a36Sopenharmony_ci struct brcmstb_usb_pinmap_data *pdata; 25362306a36Sopenharmony_ci struct in_pin *pin; 25462306a36Sopenharmony_ci struct resource *r; 25562306a36Sopenharmony_ci int out_count; 25662306a36Sopenharmony_ci int in_count; 25762306a36Sopenharmony_ci int err; 25862306a36Sopenharmony_ci int irq; 25962306a36Sopenharmony_ci int x; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci get_pin_counts(dn, &in_count, &out_count); 26262306a36Sopenharmony_ci if ((in_count + out_count) == 0) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 26662306a36Sopenharmony_ci if (!r) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci pdata = devm_kzalloc(&pdev->dev, 27062306a36Sopenharmony_ci sizeof(*pdata) + 27162306a36Sopenharmony_ci (sizeof(struct in_pin) * in_count) + 27262306a36Sopenharmony_ci (sizeof(struct out_pin) * out_count), GFP_KERNEL); 27362306a36Sopenharmony_ci if (!pdata) 27462306a36Sopenharmony_ci return -ENOMEM; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci pdata->in_count = in_count; 27762306a36Sopenharmony_ci pdata->out_count = out_count; 27862306a36Sopenharmony_ci pdata->in_pins = (struct in_pin *)(pdata + 1); 27962306a36Sopenharmony_ci pdata->out_pins = (struct out_pin *)(pdata->in_pins + in_count); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci pdata->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 28262306a36Sopenharmony_ci if (!pdata->regs) 28362306a36Sopenharmony_ci return -ENOMEM; 28462306a36Sopenharmony_ci platform_set_drvdata(pdev, pdata); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci err = parse_pins(&pdev->dev, dn, pdata); 28762306a36Sopenharmony_ci if (err) 28862306a36Sopenharmony_ci return err; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci sync_all_pins(pdata); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (out_count) { 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Enable interrupt for out pins */ 29562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 29662306a36Sopenharmony_ci if (irq < 0) 29762306a36Sopenharmony_ci return irq; 29862306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, irq, 29962306a36Sopenharmony_ci brcmstb_usb_pinmap_ovr_isr, 30062306a36Sopenharmony_ci IRQF_TRIGGER_RISING, 30162306a36Sopenharmony_ci pdev->name, pdata); 30262306a36Sopenharmony_ci if (err < 0) { 30362306a36Sopenharmony_ci dev_err(&pdev->dev, "Error requesting IRQ\n"); 30462306a36Sopenharmony_ci return err; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (x = 0, pin = pdata->in_pins; x < pdata->in_count; x++, pin++) { 30962306a36Sopenharmony_ci irq = gpiod_to_irq(pin->gpiod); 31062306a36Sopenharmony_ci if (irq < 0) { 31162306a36Sopenharmony_ci dev_err(&pdev->dev, "Error getting IRQ for %s pin\n", 31262306a36Sopenharmony_ci pin->name); 31362306a36Sopenharmony_ci return irq; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, irq, 31662306a36Sopenharmony_ci brcmstb_usb_pinmap_gpio_isr, 31762306a36Sopenharmony_ci IRQF_SHARED | IRQF_TRIGGER_RISING | 31862306a36Sopenharmony_ci IRQF_TRIGGER_FALLING, 31962306a36Sopenharmony_ci pdev->name, pin); 32062306a36Sopenharmony_ci if (err < 0) { 32162306a36Sopenharmony_ci dev_err(&pdev->dev, "Error requesting IRQ for %s pin\n", 32262306a36Sopenharmony_ci pin->name); 32362306a36Sopenharmony_ci return err; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Driver probe succeeded\n"); 32862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "In pin count: %d, out pin count: %d\n", 32962306a36Sopenharmony_ci pdata->in_count, pdata->out_count); 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic const struct of_device_id brcmstb_usb_pinmap_of_match[] = { 33562306a36Sopenharmony_ci { .compatible = "brcm,usb-pinmap" }, 33662306a36Sopenharmony_ci { }, 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic struct platform_driver brcmstb_usb_pinmap_driver = { 34062306a36Sopenharmony_ci .driver = { 34162306a36Sopenharmony_ci .name = "brcm-usb-pinmap", 34262306a36Sopenharmony_ci .of_match_table = brcmstb_usb_pinmap_of_match, 34362306a36Sopenharmony_ci }, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int __init brcmstb_usb_pinmap_init(void) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci return platform_driver_probe(&brcmstb_usb_pinmap_driver, 34962306a36Sopenharmony_ci brcmstb_usb_pinmap_probe); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cimodule_init(brcmstb_usb_pinmap_init); 35362306a36Sopenharmony_ciMODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>"); 35462306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom USB Pinmap Driver"); 35562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 356