brcmstb-usb-pinmap.c (8468B)
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2020, Broadcom */ 3 4#include <linux/init.h> 5#include <linux/types.h> 6#include <linux/module.h> 7#include <linux/platform_device.h> 8#include <linux/interrupt.h> 9#include <linux/io.h> 10#include <linux/device.h> 11#include <linux/of.h> 12#include <linux/kernel.h> 13#include <linux/kdebug.h> 14#include <linux/gpio/consumer.h> 15 16struct out_pin { 17 u32 enable_mask; 18 u32 value_mask; 19 u32 changed_mask; 20 u32 clr_changed_mask; 21 struct gpio_desc *gpiod; 22 const char *name; 23}; 24 25struct in_pin { 26 u32 enable_mask; 27 u32 value_mask; 28 struct gpio_desc *gpiod; 29 const char *name; 30 struct brcmstb_usb_pinmap_data *pdata; 31}; 32 33struct brcmstb_usb_pinmap_data { 34 void __iomem *regs; 35 int in_count; 36 struct in_pin *in_pins; 37 int out_count; 38 struct out_pin *out_pins; 39}; 40 41 42static void pinmap_set(void __iomem *reg, u32 mask) 43{ 44 u32 val; 45 46 val = readl(reg); 47 val |= mask; 48 writel(val, reg); 49} 50 51static void pinmap_unset(void __iomem *reg, u32 mask) 52{ 53 u32 val; 54 55 val = readl(reg); 56 val &= ~mask; 57 writel(val, reg); 58} 59 60static void sync_in_pin(struct in_pin *pin) 61{ 62 u32 val; 63 64 val = gpiod_get_value(pin->gpiod); 65 if (val) 66 pinmap_set(pin->pdata->regs, pin->value_mask); 67 else 68 pinmap_unset(pin->pdata->regs, pin->value_mask); 69} 70 71/* 72 * Interrupt from override register, propagate from override bit 73 * to GPIO. 74 */ 75static irqreturn_t brcmstb_usb_pinmap_ovr_isr(int irq, void *dev_id) 76{ 77 struct brcmstb_usb_pinmap_data *pdata = dev_id; 78 struct out_pin *pout; 79 u32 val; 80 u32 bit; 81 int x; 82 83 pr_debug("%s: reg: 0x%x\n", __func__, readl(pdata->regs)); 84 pout = pdata->out_pins; 85 for (x = 0; x < pdata->out_count; x++) { 86 val = readl(pdata->regs); 87 if (val & pout->changed_mask) { 88 pinmap_set(pdata->regs, pout->clr_changed_mask); 89 pinmap_unset(pdata->regs, pout->clr_changed_mask); 90 bit = val & pout->value_mask; 91 gpiod_set_value(pout->gpiod, bit ? 1 : 0); 92 pr_debug("%s: %s bit changed state to %d\n", 93 __func__, pout->name, bit ? 1 : 0); 94 } 95 } 96 return IRQ_HANDLED; 97} 98 99/* 100 * Interrupt from GPIO, propagate from GPIO to override bit. 101 */ 102static irqreturn_t brcmstb_usb_pinmap_gpio_isr(int irq, void *dev_id) 103{ 104 struct in_pin *pin = dev_id; 105 106 pr_debug("%s: %s pin changed state\n", __func__, pin->name); 107 sync_in_pin(pin); 108 return IRQ_HANDLED; 109} 110 111 112static void get_pin_counts(struct device_node *dn, int *in_count, 113 int *out_count) 114{ 115 int in; 116 int out; 117 118 *in_count = 0; 119 *out_count = 0; 120 in = of_property_count_strings(dn, "brcm,in-functions"); 121 if (in < 0) 122 return; 123 out = of_property_count_strings(dn, "brcm,out-functions"); 124 if (out < 0) 125 return; 126 *in_count = in; 127 *out_count = out; 128} 129 130static int parse_pins(struct device *dev, struct device_node *dn, 131 struct brcmstb_usb_pinmap_data *pdata) 132{ 133 struct out_pin *pout; 134 struct in_pin *pin; 135 int index; 136 int res; 137 int x; 138 139 pin = pdata->in_pins; 140 for (x = 0, index = 0; x < pdata->in_count; x++) { 141 pin->gpiod = devm_gpiod_get_index(dev, "in", x, GPIOD_IN); 142 if (IS_ERR(pin->gpiod)) { 143 dev_err(dev, "Error getting gpio %s\n", pin->name); 144 return PTR_ERR(pin->gpiod); 145 146 } 147 res = of_property_read_string_index(dn, "brcm,in-functions", x, 148 &pin->name); 149 if (res < 0) { 150 dev_err(dev, "Error getting brcm,in-functions for %s\n", 151 pin->name); 152 return res; 153 } 154 res = of_property_read_u32_index(dn, "brcm,in-masks", index++, 155 &pin->enable_mask); 156 if (res < 0) { 157 dev_err(dev, "Error getting 1st brcm,in-masks for %s\n", 158 pin->name); 159 return res; 160 } 161 res = of_property_read_u32_index(dn, "brcm,in-masks", index++, 162 &pin->value_mask); 163 if (res < 0) { 164 dev_err(dev, "Error getting 2nd brcm,in-masks for %s\n", 165 pin->name); 166 return res; 167 } 168 pin->pdata = pdata; 169 pin++; 170 } 171 pout = pdata->out_pins; 172 for (x = 0, index = 0; x < pdata->out_count; x++) { 173 pout->gpiod = devm_gpiod_get_index(dev, "out", x, 174 GPIOD_OUT_HIGH); 175 if (IS_ERR(pout->gpiod)) { 176 dev_err(dev, "Error getting gpio %s\n", pin->name); 177 return PTR_ERR(pout->gpiod); 178 } 179 res = of_property_read_string_index(dn, "brcm,out-functions", x, 180 &pout->name); 181 if (res < 0) { 182 dev_err(dev, "Error getting brcm,out-functions for %s\n", 183 pout->name); 184 return res; 185 } 186 res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 187 &pout->enable_mask); 188 if (res < 0) { 189 dev_err(dev, "Error getting 1st brcm,out-masks for %s\n", 190 pout->name); 191 return res; 192 } 193 res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 194 &pout->value_mask); 195 if (res < 0) { 196 dev_err(dev, "Error getting 2nd brcm,out-masks for %s\n", 197 pout->name); 198 return res; 199 } 200 res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 201 &pout->changed_mask); 202 if (res < 0) { 203 dev_err(dev, "Error getting 3rd brcm,out-masks for %s\n", 204 pout->name); 205 return res; 206 } 207 res = of_property_read_u32_index(dn, "brcm,out-masks", index++, 208 &pout->clr_changed_mask); 209 if (res < 0) { 210 dev_err(dev, "Error getting 4th out-masks for %s\n", 211 pout->name); 212 return res; 213 } 214 pout++; 215 } 216 return 0; 217} 218 219static void sync_all_pins(struct brcmstb_usb_pinmap_data *pdata) 220{ 221 struct out_pin *pout; 222 struct in_pin *pin; 223 int val; 224 int x; 225 226 /* 227 * Enable the override, clear any changed condition and 228 * propagate the state to the GPIO for all out pins. 229 */ 230 pout = pdata->out_pins; 231 for (x = 0; x < pdata->out_count; x++) { 232 pinmap_set(pdata->regs, pout->enable_mask); 233 pinmap_set(pdata->regs, pout->clr_changed_mask); 234 pinmap_unset(pdata->regs, pout->clr_changed_mask); 235 val = readl(pdata->regs) & pout->value_mask; 236 gpiod_set_value(pout->gpiod, val ? 1 : 0); 237 pout++; 238 } 239 240 /* sync and enable all in pins. */ 241 pin = pdata->in_pins; 242 for (x = 0; x < pdata->in_count; x++) { 243 sync_in_pin(pin); 244 pinmap_set(pdata->regs, pin->enable_mask); 245 pin++; 246 } 247} 248 249static int __init brcmstb_usb_pinmap_probe(struct platform_device *pdev) 250{ 251 struct device_node *dn = pdev->dev.of_node; 252 struct brcmstb_usb_pinmap_data *pdata; 253 struct in_pin *pin; 254 struct resource *r; 255 int out_count; 256 int in_count; 257 int err; 258 int irq; 259 int x; 260 261 get_pin_counts(dn, &in_count, &out_count); 262 if ((in_count + out_count) == 0) 263 return -EINVAL; 264 265 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 266 if (!r) 267 return -EINVAL; 268 269 pdata = devm_kzalloc(&pdev->dev, 270 sizeof(*pdata) + 271 (sizeof(struct in_pin) * in_count) + 272 (sizeof(struct out_pin) * out_count), GFP_KERNEL); 273 if (!pdata) 274 return -ENOMEM; 275 276 pdata->in_count = in_count; 277 pdata->out_count = out_count; 278 pdata->in_pins = (struct in_pin *)(pdata + 1); 279 pdata->out_pins = (struct out_pin *)(pdata->in_pins + in_count); 280 281 pdata->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 282 if (!pdata->regs) 283 return -ENOMEM; 284 platform_set_drvdata(pdev, pdata); 285 286 err = parse_pins(&pdev->dev, dn, pdata); 287 if (err) 288 return err; 289 290 sync_all_pins(pdata); 291 292 if (out_count) { 293 294 /* Enable interrupt for out pins */ 295 irq = platform_get_irq(pdev, 0); 296 if (irq < 0) 297 return irq; 298 err = devm_request_irq(&pdev->dev, irq, 299 brcmstb_usb_pinmap_ovr_isr, 300 IRQF_TRIGGER_RISING, 301 pdev->name, pdata); 302 if (err < 0) { 303 dev_err(&pdev->dev, "Error requesting IRQ\n"); 304 return err; 305 } 306 } 307 308 for (x = 0, pin = pdata->in_pins; x < pdata->in_count; x++, pin++) { 309 irq = gpiod_to_irq(pin->gpiod); 310 if (irq < 0) { 311 dev_err(&pdev->dev, "Error getting IRQ for %s pin\n", 312 pin->name); 313 return irq; 314 } 315 err = devm_request_irq(&pdev->dev, irq, 316 brcmstb_usb_pinmap_gpio_isr, 317 IRQF_SHARED | IRQF_TRIGGER_RISING | 318 IRQF_TRIGGER_FALLING, 319 pdev->name, pin); 320 if (err < 0) { 321 dev_err(&pdev->dev, "Error requesting IRQ for %s pin\n", 322 pin->name); 323 return err; 324 } 325 } 326 327 dev_dbg(&pdev->dev, "Driver probe succeeded\n"); 328 dev_dbg(&pdev->dev, "In pin count: %d, out pin count: %d\n", 329 pdata->in_count, pdata->out_count); 330 return 0; 331} 332 333 334static const struct of_device_id brcmstb_usb_pinmap_of_match[] = { 335 { .compatible = "brcm,usb-pinmap" }, 336 { }, 337}; 338 339static struct platform_driver brcmstb_usb_pinmap_driver = { 340 .driver = { 341 .name = "brcm-usb-pinmap", 342 .of_match_table = brcmstb_usb_pinmap_of_match, 343 }, 344}; 345 346static int __init brcmstb_usb_pinmap_init(void) 347{ 348 return platform_driver_probe(&brcmstb_usb_pinmap_driver, 349 brcmstb_usb_pinmap_probe); 350} 351 352module_init(brcmstb_usb_pinmap_init); 353MODULE_AUTHOR("Al Cooper <alcooperx@gmail.com>"); 354MODULE_DESCRIPTION("Broadcom USB Pinmap Driver"); 355MODULE_LICENSE("GPL");