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