1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * ULCB board CPLD access support 4 * 5 * Copyright (C) 2017 Renesas Electronics Corporation 6 * Copyright (C) 2017 Cogent Embedded, Inc. 7 */ 8 9 #include <common.h> 10 #include <asm/gpio.h> 11 #include <asm/io.h> 12 #include <dm.h> 13 #include <errno.h> 14 #include <linux/err.h> 15 #include <sysreset.h> 16 17 #define CPLD_ADDR_MODE 0x00 /* RW */ 18 #define CPLD_ADDR_MUX 0x02 /* RW */ 19 #define CPLD_ADDR_DIPSW6 0x08 /* R */ 20 #define CPLD_ADDR_RESET 0x80 /* RW */ 21 #define CPLD_ADDR_VERSION 0xFF /* R */ 22 23 struct renesas_ulcb_sysreset_priv { 24 struct gpio_desc miso; 25 struct gpio_desc mosi; 26 struct gpio_desc sck; 27 struct gpio_desc sstbz; 28 }; 29 30 static u32 cpld_read(struct udevice *dev, u8 addr) 31 { 32 struct renesas_ulcb_sysreset_priv *priv = dev_get_priv(dev); 33 u32 data = 0; 34 int i; 35 36 for (i = 0; i < 8; i++) { 37 dm_gpio_set_value(&priv->mosi, !!(addr & 0x80)); /* MSB first */ 38 dm_gpio_set_value(&priv->sck, 1); 39 addr <<= 1; 40 dm_gpio_set_value(&priv->sck, 0); 41 } 42 43 dm_gpio_set_value(&priv->mosi, 0); /* READ */ 44 dm_gpio_set_value(&priv->sstbz, 0); 45 dm_gpio_set_value(&priv->sck, 1); 46 dm_gpio_set_value(&priv->sck, 0); 47 dm_gpio_set_value(&priv->sstbz, 1); 48 49 for (i = 0; i < 32; i++) { 50 dm_gpio_set_value(&priv->sck, 1); 51 data <<= 1; 52 data |= dm_gpio_get_value(&priv->miso); /* MSB first */ 53 dm_gpio_set_value(&priv->sck, 0); 54 } 55 56 return data; 57 } 58 59 static void cpld_write(struct udevice *dev, u8 addr, u32 data) 60 { 61 struct renesas_ulcb_sysreset_priv *priv = dev_get_priv(dev); 62 int i; 63 64 for (i = 0; i < 32; i++) { 65 dm_gpio_set_value(&priv->mosi, data & (1 << 31)); /* MSB first */ 66 dm_gpio_set_value(&priv->sck, 1); 67 data <<= 1; 68 dm_gpio_set_value(&priv->sck, 0); 69 } 70 71 for (i = 0; i < 8; i++) { 72 dm_gpio_set_value(&priv->mosi, addr & 0x80); /* MSB first */ 73 dm_gpio_set_value(&priv->sck, 1); 74 addr <<= 1; 75 dm_gpio_set_value(&priv->sck, 0); 76 } 77 78 dm_gpio_set_value(&priv->mosi, 1); /* WRITE */ 79 dm_gpio_set_value(&priv->sstbz, 0); 80 dm_gpio_set_value(&priv->sck, 1); 81 dm_gpio_set_value(&priv->sck, 0); 82 dm_gpio_set_value(&priv->sstbz, 1); 83 } 84 85 static int do_cpld(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 86 { 87 struct udevice *dev; 88 u32 addr, val; 89 int ret; 90 91 ret = uclass_get_device_by_driver(UCLASS_SYSRESET, 92 DM_GET_DRIVER(sysreset_renesas_ulcb), 93 &dev); 94 if (ret) 95 return ret; 96 97 if (argc == 2 && strcmp(argv[1], "info") == 0) { 98 printf("CPLD version:\t\t\t0x%08x\n", 99 cpld_read(dev, CPLD_ADDR_VERSION)); 100 printf("H3 Mode setting (MD0..28):\t0x%08x\n", 101 cpld_read(dev, CPLD_ADDR_MODE)); 102 printf("Multiplexer settings:\t\t0x%08x\n", 103 cpld_read(dev, CPLD_ADDR_MUX)); 104 printf("DIPSW (SW6):\t\t\t0x%08x\n", 105 cpld_read(dev, CPLD_ADDR_DIPSW6)); 106 return 0; 107 } 108 109 if (argc < 3) 110 return CMD_RET_USAGE; 111 112 addr = simple_strtoul(argv[2], NULL, 16); 113 if (!(addr == CPLD_ADDR_VERSION || addr == CPLD_ADDR_MODE || 114 addr == CPLD_ADDR_MUX || addr == CPLD_ADDR_DIPSW6 || 115 addr == CPLD_ADDR_RESET)) { 116 printf("Invalid CPLD register address\n"); 117 return CMD_RET_USAGE; 118 } 119 120 if (argc == 3 && strcmp(argv[1], "read") == 0) { 121 printf("0x%x\n", cpld_read(dev, addr)); 122 } else if (argc == 4 && strcmp(argv[1], "write") == 0) { 123 val = simple_strtoul(argv[3], NULL, 16); 124 cpld_write(dev, addr, val); 125 } 126 127 return 0; 128 } 129 130 U_BOOT_CMD( 131 cpld, 4, 1, do_cpld, 132 "CPLD access", 133 "info\n" 134 "cpld read addr\n" 135 "cpld write addr val\n" 136 ); 137 138 static int renesas_ulcb_sysreset_request(struct udevice *dev, enum sysreset_t type) 139 { 140 cpld_write(dev, CPLD_ADDR_RESET, 1); 141 142 return -EINPROGRESS; 143 } 144 145 static int renesas_ulcb_sysreset_probe(struct udevice *dev) 146 { 147 struct renesas_ulcb_sysreset_priv *priv = dev_get_priv(dev); 148 149 if (gpio_request_by_name(dev, "gpio-miso", 0, &priv->miso, 150 GPIOD_IS_IN)) 151 return -EINVAL; 152 153 if (gpio_request_by_name(dev, "gpio-sck", 0, &priv->sck, 154 GPIOD_IS_OUT)) 155 return -EINVAL; 156 157 if (gpio_request_by_name(dev, "gpio-sstbz", 0, &priv->sstbz, 158 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE)) 159 return -EINVAL; 160 161 if (gpio_request_by_name(dev, "gpio-mosi", 0, &priv->mosi, 162 GPIOD_IS_OUT)) 163 return -EINVAL; 164 165 /* PULL-UP on MISO line */ 166 setbits_le32(PFC_PUEN5, PUEN_SSI_SDATA4); 167 168 /* Dummy read */ 169 cpld_read(dev, CPLD_ADDR_VERSION); 170 171 return 0; 172 } 173 174 static struct sysreset_ops renesas_ulcb_sysreset = { 175 .request = renesas_ulcb_sysreset_request, 176 }; 177 178 static const struct udevice_id renesas_ulcb_sysreset_ids[] = { 179 { .compatible = "renesas,ulcb-cpld" }, 180 { } 181 }; 182 183 U_BOOT_DRIVER(sysreset_renesas_ulcb) = { 184 .name = "renesas_ulcb_sysreset", 185 .id = UCLASS_SYSRESET, 186 .ops = &renesas_ulcb_sysreset, 187 .probe = renesas_ulcb_sysreset_probe, 188 .of_match = renesas_ulcb_sysreset_ids, 189 .priv_auto_alloc_size = sizeof(struct renesas_ulcb_sysreset_priv), 190 }; 191