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