1 /* 2 * TI da8xx master peripheral priority driver 3 * 4 * Copyright (C) 2016 BayLibre SAS 5 * 6 * Author: 7 * Bartosz Golaszewski <bgolaszewski@baylibre.com.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/platform_device.h> 17 #include <linux/io.h> 18 #include <linux/regmap.h> 19 #include <linux/of_fdt.h> 20 21 /* 22 * REVISIT: Linux doesn't have a good framework for the kind of performance 23 * knobs this driver controls. We can't use device tree properties as it deals 24 * with hardware configuration rather than description. We also don't want to 25 * commit to maintaining some random sysfs attributes. 26 * 27 * For now we just hardcode the register values for the boards that need 28 * some changes (as is the case for the LCD controller on da850-lcdk - the 29 * first board we support here). When linux gets an appropriate framework, 30 * we'll easily convert the driver to it. 31 */ 32 33 #define DA8XX_MSTPRI0_OFFSET 0 34 #define DA8XX_MSTPRI1_OFFSET 4 35 #define DA8XX_MSTPRI2_OFFSET 8 36 37 enum { 38 DA8XX_MSTPRI_ARM_I = 0, 39 DA8XX_MSTPRI_ARM_D, 40 DA8XX_MSTPRI_UPP, 41 DA8XX_MSTPRI_SATA, 42 DA8XX_MSTPRI_PRU0, 43 DA8XX_MSTPRI_PRU1, 44 DA8XX_MSTPRI_EDMA30TC0, 45 DA8XX_MSTPRI_EDMA30TC1, 46 DA8XX_MSTPRI_EDMA31TC0, 47 DA8XX_MSTPRI_VPIF_DMA_0, 48 DA8XX_MSTPRI_VPIF_DMA_1, 49 DA8XX_MSTPRI_EMAC, 50 DA8XX_MSTPRI_USB0CFG, 51 DA8XX_MSTPRI_USB0CDMA, 52 DA8XX_MSTPRI_UHPI, 53 DA8XX_MSTPRI_USB1, 54 DA8XX_MSTPRI_LCDC, 55 }; 56 57 struct da8xx_mstpri_descr { 58 int reg; 59 int shift; 60 int mask; 61 }; 62 63 static const struct da8xx_mstpri_descr da8xx_mstpri_priority_list[] = { 64 [DA8XX_MSTPRI_ARM_I] = { 65 .reg = DA8XX_MSTPRI0_OFFSET, 66 .shift = 0, 67 .mask = 0x0000000f, 68 }, 69 [DA8XX_MSTPRI_ARM_D] = { 70 .reg = DA8XX_MSTPRI0_OFFSET, 71 .shift = 4, 72 .mask = 0x000000f0, 73 }, 74 [DA8XX_MSTPRI_UPP] = { 75 .reg = DA8XX_MSTPRI0_OFFSET, 76 .shift = 16, 77 .mask = 0x000f0000, 78 }, 79 [DA8XX_MSTPRI_SATA] = { 80 .reg = DA8XX_MSTPRI0_OFFSET, 81 .shift = 20, 82 .mask = 0x00f00000, 83 }, 84 [DA8XX_MSTPRI_PRU0] = { 85 .reg = DA8XX_MSTPRI1_OFFSET, 86 .shift = 0, 87 .mask = 0x0000000f, 88 }, 89 [DA8XX_MSTPRI_PRU1] = { 90 .reg = DA8XX_MSTPRI1_OFFSET, 91 .shift = 4, 92 .mask = 0x000000f0, 93 }, 94 [DA8XX_MSTPRI_EDMA30TC0] = { 95 .reg = DA8XX_MSTPRI1_OFFSET, 96 .shift = 8, 97 .mask = 0x00000f00, 98 }, 99 [DA8XX_MSTPRI_EDMA30TC1] = { 100 .reg = DA8XX_MSTPRI1_OFFSET, 101 .shift = 12, 102 .mask = 0x0000f000, 103 }, 104 [DA8XX_MSTPRI_EDMA31TC0] = { 105 .reg = DA8XX_MSTPRI1_OFFSET, 106 .shift = 16, 107 .mask = 0x000f0000, 108 }, 109 [DA8XX_MSTPRI_VPIF_DMA_0] = { 110 .reg = DA8XX_MSTPRI1_OFFSET, 111 .shift = 24, 112 .mask = 0x0f000000, 113 }, 114 [DA8XX_MSTPRI_VPIF_DMA_1] = { 115 .reg = DA8XX_MSTPRI1_OFFSET, 116 .shift = 28, 117 .mask = 0xf0000000, 118 }, 119 [DA8XX_MSTPRI_EMAC] = { 120 .reg = DA8XX_MSTPRI2_OFFSET, 121 .shift = 0, 122 .mask = 0x0000000f, 123 }, 124 [DA8XX_MSTPRI_USB0CFG] = { 125 .reg = DA8XX_MSTPRI2_OFFSET, 126 .shift = 8, 127 .mask = 0x00000f00, 128 }, 129 [DA8XX_MSTPRI_USB0CDMA] = { 130 .reg = DA8XX_MSTPRI2_OFFSET, 131 .shift = 12, 132 .mask = 0x0000f000, 133 }, 134 [DA8XX_MSTPRI_UHPI] = { 135 .reg = DA8XX_MSTPRI2_OFFSET, 136 .shift = 20, 137 .mask = 0x00f00000, 138 }, 139 [DA8XX_MSTPRI_USB1] = { 140 .reg = DA8XX_MSTPRI2_OFFSET, 141 .shift = 24, 142 .mask = 0x0f000000, 143 }, 144 [DA8XX_MSTPRI_LCDC] = { 145 .reg = DA8XX_MSTPRI2_OFFSET, 146 .shift = 28, 147 .mask = 0xf0000000, 148 }, 149 }; 150 151 struct da8xx_mstpri_priority { 152 int which; 153 u32 val; 154 }; 155 156 struct da8xx_mstpri_board_priorities { 157 const char *board; 158 const struct da8xx_mstpri_priority *priorities; 159 size_t numprio; 160 }; 161 162 /* 163 * Default memory settings of da850 do not meet the throughput/latency 164 * requirements of tilcdc. This results in the image displayed being 165 * incorrect and the following warning being displayed by the LCDC 166 * drm driver: 167 * 168 * tilcdc da8xx_lcdc.0: tilcdc_crtc_irq(0x00000020): FIFO underfow 169 */ 170 static const struct da8xx_mstpri_priority da850_lcdk_priorities[] = { 171 { 172 .which = DA8XX_MSTPRI_LCDC, 173 .val = 0, 174 }, 175 { 176 .which = DA8XX_MSTPRI_EDMA30TC1, 177 .val = 0, 178 }, 179 { 180 .which = DA8XX_MSTPRI_EDMA30TC0, 181 .val = 1, 182 }, 183 }; 184 185 static const struct da8xx_mstpri_board_priorities da8xx_mstpri_board_confs[] = { 186 { 187 .board = "ti,da850-lcdk", 188 .priorities = da850_lcdk_priorities, 189 .numprio = ARRAY_SIZE(da850_lcdk_priorities), 190 }, 191 }; 192 193 static const struct da8xx_mstpri_board_priorities * 194 da8xx_mstpri_get_board_prio(void) 195 { 196 const struct da8xx_mstpri_board_priorities *board_prio; 197 int i; 198 199 for (i = 0; i < ARRAY_SIZE(da8xx_mstpri_board_confs); i++) { 200 board_prio = &da8xx_mstpri_board_confs[i]; 201 202 if (of_machine_is_compatible(board_prio->board)) 203 return board_prio; 204 } 205 206 return NULL; 207 } 208 209 static int da8xx_mstpri_probe(struct platform_device *pdev) 210 { 211 const struct da8xx_mstpri_board_priorities *prio_list; 212 const struct da8xx_mstpri_descr *prio_descr; 213 const struct da8xx_mstpri_priority *prio; 214 struct device *dev = &pdev->dev; 215 struct resource *res; 216 void __iomem *mstpri; 217 u32 reg; 218 int i; 219 220 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 221 mstpri = devm_ioremap_resource(dev, res); 222 if (IS_ERR(mstpri)) { 223 dev_err(dev, "unable to map MSTPRI registers\n"); 224 return PTR_ERR(mstpri); 225 } 226 227 prio_list = da8xx_mstpri_get_board_prio(); 228 if (!prio_list) { 229 dev_err(dev, "no master priotities defined for board '%s'\n", 230 of_flat_dt_get_machine_name()); 231 return -EINVAL; 232 } 233 234 for (i = 0; i < prio_list->numprio; i++) { 235 prio = &prio_list->priorities[i]; 236 prio_descr = &da8xx_mstpri_priority_list[prio->which]; 237 238 if (prio_descr->reg + sizeof(u32) > resource_size(res)) { 239 dev_warn(dev, "register offset out of range\n"); 240 continue; 241 } 242 243 reg = readl(mstpri + prio_descr->reg); 244 reg &= ~prio_descr->mask; 245 reg |= prio->val << prio_descr->shift; 246 247 writel(reg, mstpri + prio_descr->reg); 248 } 249 250 return 0; 251 } 252 253 static const struct of_device_id da8xx_mstpri_of_match[] = { 254 { .compatible = "ti,da850-mstpri", }, 255 { }, 256 }; 257 258 static struct platform_driver da8xx_mstpri_driver = { 259 .probe = da8xx_mstpri_probe, 260 .driver = { 261 .name = "da8xx-mstpri", 262 .of_match_table = da8xx_mstpri_of_match, 263 }, 264 }; 265 module_platform_driver(da8xx_mstpri_driver); 266 267 MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); 268 MODULE_DESCRIPTION("TI da8xx master peripheral priority driver"); 269 MODULE_LICENSE("GPL v2"); 270