1ad309a88SDingqiang Lin /*
2ad309a88SDingqiang Lin * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
3ad309a88SDingqiang Lin *
4ba0501acSDingqiang Lin * SPDX-License-Identifier: GPL-2.0
5ad309a88SDingqiang Lin */
6ba0501acSDingqiang Lin
7ad309a88SDingqiang Lin #include <common.h>
8ad309a88SDingqiang Lin #include <dm.h>
9ad309a88SDingqiang Lin
10ad309a88SDingqiang Lin #include "rkflash_api.h"
11ad309a88SDingqiang Lin #include "rkflash_blk.h"
12*f3a2c32eSJon Lin #include "rkflash_debug.h"
13ad309a88SDingqiang Lin
14ad309a88SDingqiang Lin #ifdef CONFIG_RKSFC_NOR
15*f3a2c32eSJon Lin
16*f3a2c32eSJon Lin #define IDB_ALIGN_64 128 /* 64 KB */
17*f3a2c32eSJon Lin #define IDB_ALIGN_32 64 /* 32 KB */
18*f3a2c32eSJon Lin
P_RC4(u8 * buf,u16 len)19*f3a2c32eSJon Lin static void P_RC4(u8 *buf, u16 len)
20*f3a2c32eSJon Lin {
21*f3a2c32eSJon Lin u8 S[256], K[256], temp;
22*f3a2c32eSJon Lin u16 i, j, t, x;
23*f3a2c32eSJon Lin u8 key[16] = {124, 78, 3, 4, 85, 5, 9, 7,
24*f3a2c32eSJon Lin 45, 44, 123, 56, 23, 13, 23, 17};
25*f3a2c32eSJon Lin
26*f3a2c32eSJon Lin j = 0;
27*f3a2c32eSJon Lin for (i = 0; i < 256; i++) {
28*f3a2c32eSJon Lin S[i] = (u8)i;
29*f3a2c32eSJon Lin j &= 0x0f;
30*f3a2c32eSJon Lin K[i] = key[j];
31*f3a2c32eSJon Lin j++;
32*f3a2c32eSJon Lin }
33*f3a2c32eSJon Lin
34*f3a2c32eSJon Lin j = 0;
35*f3a2c32eSJon Lin for (i = 0; i < 256; i++) {
36*f3a2c32eSJon Lin j = (j + S[i] + K[i]) % 256;
37*f3a2c32eSJon Lin temp = S[i];
38*f3a2c32eSJon Lin S[i] = S[j];
39*f3a2c32eSJon Lin S[j] = temp;
40*f3a2c32eSJon Lin }
41*f3a2c32eSJon Lin
42*f3a2c32eSJon Lin i = 0;
43*f3a2c32eSJon Lin j = 0;
44*f3a2c32eSJon Lin for (x = 0; x < len; x++) {
45*f3a2c32eSJon Lin i = (i + 1) % 256;
46*f3a2c32eSJon Lin j = (j + S[i]) % 256;
47*f3a2c32eSJon Lin temp = S[i];
48*f3a2c32eSJon Lin S[i] = S[j];
49*f3a2c32eSJon Lin S[j] = temp;
50*f3a2c32eSJon Lin t = (S[i] + (S[j] % 256)) % 256;
51*f3a2c32eSJon Lin buf[x] = buf[x] ^ S[t];
52*f3a2c32eSJon Lin }
53*f3a2c32eSJon Lin }
54*f3a2c32eSJon Lin
rksfc_nor_init(struct udevice * udev)55ba0501acSDingqiang Lin int rksfc_nor_init(struct udevice *udev)
56ad309a88SDingqiang Lin {
57ad309a88SDingqiang Lin struct rkflash_info *priv = dev_get_priv(udev);
58ad309a88SDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
59*f3a2c32eSJon Lin struct snor_info_packet *packet;
60*f3a2c32eSJon Lin struct id_block_tag *idb_tag;
61*f3a2c32eSJon Lin int ret;
62ad309a88SDingqiang Lin
63*f3a2c32eSJon Lin ret = snor_init(p_dev);
64*f3a2c32eSJon Lin if (ret == SFC_OK && p_dev->read_lines == DATA_LINES_X1) {
65*f3a2c32eSJon Lin idb_tag = kzalloc(NOR_SECS_PAGE * 512, GFP_KERNEL);
66*f3a2c32eSJon Lin if (!idb_tag)
67*f3a2c32eSJon Lin return SFC_OK;
68*f3a2c32eSJon Lin
69*f3a2c32eSJon Lin if (sfc_get_version() >= SFC_VER_4)
70*f3a2c32eSJon Lin snor_read(p_dev, IDB_ALIGN_32, NOR_SECS_PAGE,
71*f3a2c32eSJon Lin idb_tag);
72*f3a2c32eSJon Lin else
73*f3a2c32eSJon Lin snor_read(p_dev, IDB_ALIGN_64, NOR_SECS_PAGE,
74*f3a2c32eSJon Lin idb_tag);
75*f3a2c32eSJon Lin packet = (struct snor_info_packet *)&idb_tag->dev_param[0];
76*f3a2c32eSJon Lin if (idb_tag->id == IDB_BLOCK_TAG_ID) {
77*f3a2c32eSJon Lin P_RC4((u8 *)idb_tag, sizeof(struct id_block_tag));
78*f3a2c32eSJon Lin snor_reinit_from_table_packet(p_dev, packet);
79*f3a2c32eSJon Lin rkflash_print_error("snor reinit, ret= %d\n", ret);
80*f3a2c32eSJon Lin }
81*f3a2c32eSJon Lin kfree(idb_tag);
82*f3a2c32eSJon Lin }
83*f3a2c32eSJon Lin
84*f3a2c32eSJon Lin return ret;
85ad309a88SDingqiang Lin }
86ad309a88SDingqiang Lin
rksfc_nor_get_capacity(struct udevice * udev)87ba0501acSDingqiang Lin u32 rksfc_nor_get_capacity(struct udevice *udev)
88ad309a88SDingqiang Lin {
89ad309a88SDingqiang Lin struct rkflash_info *priv = dev_get_priv(udev);
90ad309a88SDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
91ad309a88SDingqiang Lin
92ad309a88SDingqiang Lin return snor_get_capacity(p_dev);
93ad309a88SDingqiang Lin }
94ad309a88SDingqiang Lin
rksfc_nor_read(struct udevice * udev,u32 sec,u32 n_sec,void * p_data)95ba0501acSDingqiang Lin int rksfc_nor_read(struct udevice *udev, u32 sec, u32 n_sec, void *p_data)
96ad309a88SDingqiang Lin {
97ba0501acSDingqiang Lin u32 ret;
98ba0501acSDingqiang Lin u32 offset, count = 0;
99ba0501acSDingqiang Lin char *buf = (char *)p_data;
100ad309a88SDingqiang Lin struct rkflash_info *priv = dev_get_priv(udev);
101ad309a88SDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
102ad309a88SDingqiang Lin
103ba0501acSDingqiang Lin if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
104ba0501acSDingqiang Lin sec > FLASH_VENDOR_PART_END) {
105ba0501acSDingqiang Lin ret = snor_read(p_dev, sec, n_sec, p_data);
106ba0501acSDingqiang Lin if (ret != n_sec)
107ba0501acSDingqiang Lin return ret;
108ba0501acSDingqiang Lin } else {
109ba0501acSDingqiang Lin memset(p_data, 0, 512 * n_sec);
110ba0501acSDingqiang Lin if (sec < FLASH_VENDOR_PART_START) {
111ba0501acSDingqiang Lin count = FLASH_VENDOR_PART_START - sec;
112ba0501acSDingqiang Lin buf = (char *)p_data;
113ba0501acSDingqiang Lin ret = snor_read(p_dev, sec, count, buf);
114ba0501acSDingqiang Lin if (ret != count)
115ba0501acSDingqiang Lin return ret;
116ba0501acSDingqiang Lin }
117ba0501acSDingqiang Lin if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
118ba0501acSDingqiang Lin count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
119ba0501acSDingqiang Lin offset = FLASH_VENDOR_PART_END - sec + 1;
120ba0501acSDingqiang Lin buf = (char *)p_data + offset * 512;
121ba0501acSDingqiang Lin ret = snor_read(p_dev,
122ba0501acSDingqiang Lin FLASH_VENDOR_PART_END + 1,
123ba0501acSDingqiang Lin count, buf);
124ba0501acSDingqiang Lin if (ret != count)
125ba0501acSDingqiang Lin return ret;
126ba0501acSDingqiang Lin }
127ba0501acSDingqiang Lin }
128ba0501acSDingqiang Lin
129ba0501acSDingqiang Lin return n_sec;
130ba0501acSDingqiang Lin }
131ba0501acSDingqiang Lin
1327edaca22SJon Lin /* Workaround for GPT not aligned program */
rksfc_nor_simply_over_write(struct udevice * udev,u32 sec,u32 n_sec,const void * p_data)1337edaca22SJon Lin int rksfc_nor_simply_over_write(struct udevice *udev,
1347edaca22SJon Lin u32 sec,
1357edaca22SJon Lin u32 n_sec,
1367edaca22SJon Lin const void *p_data)
1377edaca22SJon Lin {
1387edaca22SJon Lin struct rkflash_info *priv = dev_get_priv(udev);
1397edaca22SJon Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
1407edaca22SJon Lin u8 *pbuf_temp;
1417edaca22SJon Lin u32 addr_aligned, offset, remain;
1427edaca22SJon Lin
1437edaca22SJon Lin addr_aligned = sec / NOR_SECS_PAGE * NOR_SECS_PAGE;
1447edaca22SJon Lin offset = sec - addr_aligned;
1457edaca22SJon Lin remain = (offset + n_sec + NOR_SECS_PAGE - 1) / NOR_SECS_PAGE * NOR_SECS_PAGE;
1467edaca22SJon Lin
1477edaca22SJon Lin pbuf_temp = malloc(remain * 512);
1487edaca22SJon Lin snor_read(p_dev, addr_aligned, remain, pbuf_temp);
1497edaca22SJon Lin memcpy(pbuf_temp + offset * 512, p_data, n_sec * 512);
1507edaca22SJon Lin snor_write(p_dev, addr_aligned, remain, pbuf_temp);
1517edaca22SJon Lin free(pbuf_temp);
1527edaca22SJon Lin
1537edaca22SJon Lin return n_sec;
1547edaca22SJon Lin }
1557edaca22SJon Lin
rksfc_nor_write(struct udevice * udev,u32 sec,u32 n_sec,const void * p_data)156ba0501acSDingqiang Lin int rksfc_nor_write(struct udevice *udev,
157ba0501acSDingqiang Lin u32 sec,
158ba0501acSDingqiang Lin u32 n_sec,
159ba0501acSDingqiang Lin const void *p_data)
160ba0501acSDingqiang Lin {
161ba0501acSDingqiang Lin u32 ret;
162ba0501acSDingqiang Lin u32 offset, count = 0;
163ba0501acSDingqiang Lin char *buf = (char *)p_data;
164ba0501acSDingqiang Lin struct rkflash_info *priv = dev_get_priv(udev);
165ba0501acSDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
1667edaca22SJon Lin u32 sfc_nor_density = rksfc_nor_get_capacity(udev);
1677edaca22SJon Lin
1687edaca22SJon Lin if (sec >= (sfc_nor_density - 33))
1697edaca22SJon Lin return rksfc_nor_simply_over_write(udev, sec, n_sec, p_data);
170ba0501acSDingqiang Lin
171ba0501acSDingqiang Lin if (sec + n_sec - 1 < FLASH_VENDOR_PART_START ||
172ba0501acSDingqiang Lin sec > FLASH_VENDOR_PART_END) {
173c84f0ed8SJon Lin ret = snor_write(p_dev, sec, n_sec, (void *)p_data);
174ba0501acSDingqiang Lin if (ret != n_sec)
175ba0501acSDingqiang Lin return ret;
176ba0501acSDingqiang Lin } else {
177ba0501acSDingqiang Lin if (sec < FLASH_VENDOR_PART_START) {
178ba0501acSDingqiang Lin count = FLASH_VENDOR_PART_START - sec;
179ba0501acSDingqiang Lin buf = (char *)p_data;
180ba0501acSDingqiang Lin ret = snor_write(p_dev, sec, count, buf);
181ba0501acSDingqiang Lin if (ret != count)
182ba0501acSDingqiang Lin return ret;
183ba0501acSDingqiang Lin }
184ba0501acSDingqiang Lin if ((sec + n_sec - 1) > FLASH_VENDOR_PART_END) {
185ba0501acSDingqiang Lin count = sec + n_sec - 1 - FLASH_VENDOR_PART_END;
186ba0501acSDingqiang Lin offset = FLASH_VENDOR_PART_END - sec + 1;
187ba0501acSDingqiang Lin buf = (char *)p_data + offset * 512;
188ba0501acSDingqiang Lin ret = snor_write(p_dev,
189ba0501acSDingqiang Lin FLASH_VENDOR_PART_END + 1,
190ba0501acSDingqiang Lin count, buf);
191ba0501acSDingqiang Lin if (ret != count)
192ba0501acSDingqiang Lin return ret;
193ba0501acSDingqiang Lin }
194ba0501acSDingqiang Lin }
195ba0501acSDingqiang Lin
196ba0501acSDingqiang Lin return n_sec;
197ba0501acSDingqiang Lin }
198ba0501acSDingqiang Lin
rksfc_nor_vendor_read(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)199ba0501acSDingqiang Lin int rksfc_nor_vendor_read(struct blk_desc *dev_desc,
200ba0501acSDingqiang Lin u32 sec,
201ba0501acSDingqiang Lin u32 n_sec,
202ba0501acSDingqiang Lin void *p_data)
203ba0501acSDingqiang Lin {
204ba0501acSDingqiang Lin struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
205ba0501acSDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
206ba0501acSDingqiang Lin
207ad309a88SDingqiang Lin return snor_read(p_dev, sec, n_sec, p_data);
208ad309a88SDingqiang Lin }
209ad309a88SDingqiang Lin
rksfc_nor_vendor_write(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)210ba0501acSDingqiang Lin int rksfc_nor_vendor_write(struct blk_desc *dev_desc,
211ba0501acSDingqiang Lin u32 sec,
212ba0501acSDingqiang Lin u32 n_sec,
213ba0501acSDingqiang Lin void *p_data)
214ad309a88SDingqiang Lin {
215ba0501acSDingqiang Lin struct rkflash_info *priv = dev_get_priv(dev_desc->bdev->parent);
216ad309a88SDingqiang Lin struct SFNOR_DEV *p_dev = (struct SFNOR_DEV *)&priv->flash_dev_info;
217ad309a88SDingqiang Lin
218ad309a88SDingqiang Lin return snor_write(p_dev, sec, n_sec, p_data);
219ad309a88SDingqiang Lin }
220ba0501acSDingqiang Lin
221ad309a88SDingqiang Lin #endif
222ad309a88SDingqiang Lin
223ba0501acSDingqiang Lin #ifdef CONFIG_RKSFC_NAND
rksfc_nand_init(struct udevice * udev)224ba0501acSDingqiang Lin int rksfc_nand_init(struct udevice *udev)
225ba0501acSDingqiang Lin {
226ba0501acSDingqiang Lin int ret;
227ba0501acSDingqiang Lin
228ba0501acSDingqiang Lin ret = sfc_nand_init();
229c84f0ed8SJon Lin if (ret) {
230ba0501acSDingqiang Lin return ret;
231c84f0ed8SJon Lin } else {
232c84f0ed8SJon Lin sfc_nand_ftl_ops_init();
233c84f0ed8SJon Lin
234ba0501acSDingqiang Lin return sftl_init();
235ba0501acSDingqiang Lin }
236c84f0ed8SJon Lin }
237ba0501acSDingqiang Lin
rksfc_nand_read(struct udevice * udev,u32 index,u32 count,void * buf)238ba0501acSDingqiang Lin int rksfc_nand_read(struct udevice *udev, u32 index, u32 count, void *buf)
239ba0501acSDingqiang Lin {
240ba0501acSDingqiang Lin int ret;
241ba0501acSDingqiang Lin
242ba0501acSDingqiang Lin ret = sftl_read(index, count, (u8 *)buf);
243ba0501acSDingqiang Lin if (!ret)
244ba0501acSDingqiang Lin return count;
245ba0501acSDingqiang Lin else
246ba0501acSDingqiang Lin return -EIO;
247ba0501acSDingqiang Lin }
248ba0501acSDingqiang Lin
rksfc_nand_write(struct udevice * udev,u32 index,u32 count,const void * buf)249ba0501acSDingqiang Lin int rksfc_nand_write(struct udevice *udev,
250ba0501acSDingqiang Lin u32 index,
251ba0501acSDingqiang Lin u32 count,
252ba0501acSDingqiang Lin const void *buf)
253ba0501acSDingqiang Lin {
254ba0501acSDingqiang Lin int ret;
255ba0501acSDingqiang Lin
256ba0501acSDingqiang Lin ret = sftl_write(index, count, (u8 *)buf);
257ba0501acSDingqiang Lin if (!ret)
258ba0501acSDingqiang Lin return count;
259ba0501acSDingqiang Lin else
260ba0501acSDingqiang Lin return -EIO;
261ba0501acSDingqiang Lin }
262ba0501acSDingqiang Lin
rksfc_nand_get_density(struct udevice * udev)263ba0501acSDingqiang Lin u32 rksfc_nand_get_density(struct udevice *udev)
264ba0501acSDingqiang Lin {
265ba0501acSDingqiang Lin return sftl_get_density();
266ba0501acSDingqiang Lin }
26740bd3f86SDingqiang Lin
rksfc_nand_vendor_read(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)26840bd3f86SDingqiang Lin int rksfc_nand_vendor_read(struct blk_desc *dev_desc,
26940bd3f86SDingqiang Lin u32 sec,
27040bd3f86SDingqiang Lin u32 n_sec,
27140bd3f86SDingqiang Lin void *p_data)
27240bd3f86SDingqiang Lin {
27340bd3f86SDingqiang Lin int ret;
27440bd3f86SDingqiang Lin
27540bd3f86SDingqiang Lin ret = sftl_vendor_read(sec, n_sec, (u8 *)p_data);
27640bd3f86SDingqiang Lin if (!ret)
27740bd3f86SDingqiang Lin return n_sec;
27840bd3f86SDingqiang Lin else
27940bd3f86SDingqiang Lin return -EIO;
28040bd3f86SDingqiang Lin }
28140bd3f86SDingqiang Lin
rksfc_nand_vendor_write(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)28240bd3f86SDingqiang Lin int rksfc_nand_vendor_write(struct blk_desc *dev_desc,
28340bd3f86SDingqiang Lin u32 sec,
28440bd3f86SDingqiang Lin u32 n_sec,
28540bd3f86SDingqiang Lin void *p_data)
28640bd3f86SDingqiang Lin {
28740bd3f86SDingqiang Lin int ret;
28840bd3f86SDingqiang Lin
28940bd3f86SDingqiang Lin ret = sftl_vendor_write(sec, n_sec, (u8 *)p_data);
29040bd3f86SDingqiang Lin if (!ret)
29140bd3f86SDingqiang Lin return n_sec;
29240bd3f86SDingqiang Lin else
29340bd3f86SDingqiang Lin return -EIO;
29440bd3f86SDingqiang Lin }
29540bd3f86SDingqiang Lin
296ba0501acSDingqiang Lin #endif
297ba0501acSDingqiang Lin
298ba0501acSDingqiang Lin #ifdef CONFIG_RKNANDC_NAND
rknand_flash_init(struct udevice * udev)299ba0501acSDingqiang Lin int rknand_flash_init(struct udevice *udev)
300ba0501acSDingqiang Lin {
301ba0501acSDingqiang Lin return sftl_init();
302ba0501acSDingqiang Lin }
303ba0501acSDingqiang Lin
rknand_flash_read(struct udevice * udev,u32 index,u32 count,void * buf)304ba0501acSDingqiang Lin int rknand_flash_read(struct udevice *udev, u32 index, u32 count, void *buf)
305ba0501acSDingqiang Lin {
306ba0501acSDingqiang Lin int ret;
307ba0501acSDingqiang Lin
308ba0501acSDingqiang Lin ret = sftl_read(index, count, (u8 *)buf);
309ba0501acSDingqiang Lin if (!ret)
310ba0501acSDingqiang Lin return count;
311ba0501acSDingqiang Lin else
312ba0501acSDingqiang Lin return -EIO;
313ba0501acSDingqiang Lin }
314ba0501acSDingqiang Lin
rknand_flash_write(struct udevice * udev,u32 index,u32 count,const void * buf)315ba0501acSDingqiang Lin int rknand_flash_write(struct udevice *udev,
316ba0501acSDingqiang Lin u32 index,
317ba0501acSDingqiang Lin u32 count,
318ba0501acSDingqiang Lin const void *buf)
319ba0501acSDingqiang Lin {
320ba0501acSDingqiang Lin int ret;
321ba0501acSDingqiang Lin
322ba0501acSDingqiang Lin ret = sftl_write(index, count, (u8 *)buf);
323ba0501acSDingqiang Lin if (!ret)
324ba0501acSDingqiang Lin return count;
325ba0501acSDingqiang Lin else
326ba0501acSDingqiang Lin return -EIO;
327ba0501acSDingqiang Lin }
328ba0501acSDingqiang Lin
rknand_flash_get_density(struct udevice * udev)329ba0501acSDingqiang Lin u32 rknand_flash_get_density(struct udevice *udev)
330ba0501acSDingqiang Lin {
331ba0501acSDingqiang Lin return sftl_get_density();
332ba0501acSDingqiang Lin }
33340bd3f86SDingqiang Lin
rknand_flash_vendor_read(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)33440bd3f86SDingqiang Lin int rknand_flash_vendor_read(struct blk_desc *dev_desc,
33540bd3f86SDingqiang Lin u32 sec,
33640bd3f86SDingqiang Lin u32 n_sec,
33740bd3f86SDingqiang Lin void *p_data)
33840bd3f86SDingqiang Lin {
33940bd3f86SDingqiang Lin int ret;
34040bd3f86SDingqiang Lin
34140bd3f86SDingqiang Lin ret = sftl_vendor_read(sec, n_sec, (u8 *)p_data);
34240bd3f86SDingqiang Lin if (!ret)
34340bd3f86SDingqiang Lin return n_sec;
34440bd3f86SDingqiang Lin else
34540bd3f86SDingqiang Lin return -EIO;
34640bd3f86SDingqiang Lin }
34740bd3f86SDingqiang Lin
rknand_flash_vendor_write(struct blk_desc * dev_desc,u32 sec,u32 n_sec,void * p_data)34840bd3f86SDingqiang Lin int rknand_flash_vendor_write(struct blk_desc *dev_desc,
34940bd3f86SDingqiang Lin u32 sec,
35040bd3f86SDingqiang Lin u32 n_sec,
35140bd3f86SDingqiang Lin void *p_data)
35240bd3f86SDingqiang Lin {
35340bd3f86SDingqiang Lin int ret;
35440bd3f86SDingqiang Lin
35540bd3f86SDingqiang Lin ret = sftl_vendor_write(sec, n_sec, (u8 *)p_data);
35640bd3f86SDingqiang Lin if (!ret)
35740bd3f86SDingqiang Lin return n_sec;
35840bd3f86SDingqiang Lin else
35940bd3f86SDingqiang Lin return -EIO;
36040bd3f86SDingqiang Lin }
36140bd3f86SDingqiang Lin
362ba0501acSDingqiang Lin #endif
363