1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * SPEAr platform SPI chipselect abstraction over gpiolib 4 * 5 * Copyright (C) 2012 ST Microelectronics 6 * Shiraz Hashim <shiraz.linux.kernel@gmail.com> 7 */ 8 9 #include <linux/err.h> 10 #include <linux/gpio/driver.h> 11 #include <linux/io.h> 12 #include <linux/init.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/types.h> 16 17 /* maximum chipselects */ 18 #define NUM_OF_GPIO 4 19 20 /* 21 * Provision is available on some SPEAr SoCs to control ARM PL022 spi cs 22 * through system registers. This register lies outside spi (pl022) 23 * address space into system registers. 24 * 25 * It provides control for spi chip select lines so that any chipselect 26 * (out of 4 possible chipselects in pl022) can be made low to select 27 * the particular slave. 28 */ 29 30 /** 31 * struct spear_spics - represents spi chip select control 32 * @base: base address 33 * @perip_cfg: configuration register 34 * @sw_enable_bit: bit to enable s/w control over chipselects 35 * @cs_value_bit: bit to program high or low chipselect 36 * @cs_enable_mask: mask to select bits required to select chipselect 37 * @cs_enable_shift: bit pos of cs_enable_mask 38 * @use_count: use count of a spi controller cs lines 39 * @last_off: stores last offset caller of set_value() 40 * @chip: gpio_chip abstraction 41 */ 42 struct spear_spics { 43 void __iomem *base; 44 u32 perip_cfg; 45 u32 sw_enable_bit; 46 u32 cs_value_bit; 47 u32 cs_enable_mask; 48 u32 cs_enable_shift; 49 unsigned long use_count; 50 int last_off; 51 struct gpio_chip chip; 52 }; 53 54 /* gpio framework specific routines */ 55 static int spics_get_value(struct gpio_chip *chip, unsigned offset) 56 { 57 return -ENXIO; 58 } 59 60 static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) 61 { 62 struct spear_spics *spics = gpiochip_get_data(chip); 63 u32 tmp; 64 65 /* select chip select from register */ 66 tmp = readl_relaxed(spics->base + spics->perip_cfg); 67 if (spics->last_off != offset) { 68 spics->last_off = offset; 69 tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift); 70 tmp |= offset << spics->cs_enable_shift; 71 } 72 73 /* toggle chip select line */ 74 tmp &= ~(0x1 << spics->cs_value_bit); 75 tmp |= value << spics->cs_value_bit; 76 writel_relaxed(tmp, spics->base + spics->perip_cfg); 77 } 78 79 static int spics_direction_input(struct gpio_chip *chip, unsigned offset) 80 { 81 return -ENXIO; 82 } 83 84 static int spics_direction_output(struct gpio_chip *chip, unsigned offset, 85 int value) 86 { 87 spics_set_value(chip, offset, value); 88 return 0; 89 } 90 91 static int spics_request(struct gpio_chip *chip, unsigned offset) 92 { 93 struct spear_spics *spics = gpiochip_get_data(chip); 94 u32 tmp; 95 96 if (!spics->use_count++) { 97 tmp = readl_relaxed(spics->base + spics->perip_cfg); 98 tmp |= 0x1 << spics->sw_enable_bit; 99 tmp |= 0x1 << spics->cs_value_bit; 100 writel_relaxed(tmp, spics->base + spics->perip_cfg); 101 } 102 103 return 0; 104 } 105 106 static void spics_free(struct gpio_chip *chip, unsigned offset) 107 { 108 struct spear_spics *spics = gpiochip_get_data(chip); 109 u32 tmp; 110 111 if (!--spics->use_count) { 112 tmp = readl_relaxed(spics->base + spics->perip_cfg); 113 tmp &= ~(0x1 << spics->sw_enable_bit); 114 writel_relaxed(tmp, spics->base + spics->perip_cfg); 115 } 116 } 117 118 static int spics_gpio_probe(struct platform_device *pdev) 119 { 120 struct device_node *np = pdev->dev.of_node; 121 struct spear_spics *spics; 122 123 spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL); 124 if (!spics) 125 return -ENOMEM; 126 127 spics->base = devm_platform_ioremap_resource(pdev, 0); 128 if (IS_ERR(spics->base)) 129 return PTR_ERR(spics->base); 130 131 if (of_property_read_u32(np, "st-spics,peripcfg-reg", 132 &spics->perip_cfg)) 133 goto err_dt_data; 134 if (of_property_read_u32(np, "st-spics,sw-enable-bit", 135 &spics->sw_enable_bit)) 136 goto err_dt_data; 137 if (of_property_read_u32(np, "st-spics,cs-value-bit", 138 &spics->cs_value_bit)) 139 goto err_dt_data; 140 if (of_property_read_u32(np, "st-spics,cs-enable-mask", 141 &spics->cs_enable_mask)) 142 goto err_dt_data; 143 if (of_property_read_u32(np, "st-spics,cs-enable-shift", 144 &spics->cs_enable_shift)) 145 goto err_dt_data; 146 147 spics->chip.ngpio = NUM_OF_GPIO; 148 spics->chip.base = -1; 149 spics->chip.request = spics_request; 150 spics->chip.free = spics_free; 151 spics->chip.direction_input = spics_direction_input; 152 spics->chip.direction_output = spics_direction_output; 153 spics->chip.get = spics_get_value; 154 spics->chip.set = spics_set_value; 155 spics->chip.label = dev_name(&pdev->dev); 156 spics->chip.parent = &pdev->dev; 157 spics->chip.owner = THIS_MODULE; 158 spics->last_off = -1; 159 160 return devm_gpiochip_add_data(&pdev->dev, &spics->chip, spics); 161 162 err_dt_data: 163 dev_err(&pdev->dev, "DT probe failed\n"); 164 return -EINVAL; 165 } 166 167 static const struct of_device_id spics_gpio_of_match[] = { 168 { .compatible = "st,spear-spics-gpio" }, 169 {} 170 }; 171 172 static struct platform_driver spics_gpio_driver = { 173 .probe = spics_gpio_probe, 174 .driver = { 175 .name = "spear-spics-gpio", 176 .of_match_table = spics_gpio_of_match, 177 }, 178 }; 179 180 static int __init spics_gpio_init(void) 181 { 182 return platform_driver_register(&spics_gpio_driver); 183 } 184 subsys_initcall(spics_gpio_init); 185